@tsonic/emitter 0.0.72 → 0.0.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-generator.d.ts.map +1 -1
  3. package/dist/adapter-generator.js +2 -1
  4. package/dist/adapter-generator.js.map +1 -1
  5. package/dist/constants.d.ts +3 -2
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +15 -6
  8. package/dist/constants.js.map +1 -1
  9. package/dist/core/format/attributes.d.ts.map +1 -1
  10. package/dist/core/format/attributes.js +5 -56
  11. package/dist/core/format/attributes.js.map +1 -1
  12. package/dist/core/format/attributes.test.js +1 -1
  13. package/dist/core/format/attributes.test.js.map +1 -1
  14. package/dist/core/format/backend-ast/builders.d.ts +13 -0
  15. package/dist/core/format/backend-ast/builders.d.ts.map +1 -0
  16. package/dist/core/format/backend-ast/builders.js +169 -0
  17. package/dist/core/format/backend-ast/builders.js.map +1 -0
  18. package/dist/core/format/backend-ast/builders.test.d.ts +2 -0
  19. package/dist/core/format/backend-ast/builders.test.d.ts.map +1 -0
  20. package/dist/core/format/backend-ast/builders.test.js +258 -0
  21. package/dist/core/format/backend-ast/builders.test.js.map +1 -0
  22. package/dist/core/format/backend-ast/index.d.ts +3 -2
  23. package/dist/core/format/backend-ast/index.d.ts.map +1 -1
  24. package/dist/core/format/backend-ast/index.js +2 -1
  25. package/dist/core/format/backend-ast/index.js.map +1 -1
  26. package/dist/core/format/backend-ast/invariants.test.d.ts +2 -0
  27. package/dist/core/format/backend-ast/invariants.test.d.ts.map +1 -0
  28. package/dist/core/format/backend-ast/invariants.test.js +72 -0
  29. package/dist/core/format/backend-ast/invariants.test.js.map +1 -0
  30. package/dist/core/format/backend-ast/printer.d.ts +1 -1
  31. package/dist/core/format/backend-ast/printer.d.ts.map +1 -1
  32. package/dist/core/format/backend-ast/printer.js +362 -119
  33. package/dist/core/format/backend-ast/printer.js.map +1 -1
  34. package/dist/core/format/backend-ast/printer.test.d.ts +2 -0
  35. package/dist/core/format/backend-ast/printer.test.d.ts.map +1 -0
  36. package/dist/core/format/backend-ast/printer.test.js +796 -0
  37. package/dist/core/format/backend-ast/printer.test.js.map +1 -0
  38. package/dist/core/format/backend-ast/types.d.ts +66 -14
  39. package/dist/core/format/backend-ast/types.d.ts.map +1 -1
  40. package/dist/core/format/backend-ast/utils.d.ts +12 -8
  41. package/dist/core/format/backend-ast/utils.d.ts.map +1 -1
  42. package/dist/core/format/backend-ast/utils.js +222 -19
  43. package/dist/core/format/backend-ast/utils.js.map +1 -1
  44. package/dist/core/format/backend-ast/utils.test.d.ts +2 -0
  45. package/dist/core/format/backend-ast/utils.test.d.ts.map +1 -0
  46. package/dist/core/format/backend-ast/utils.test.js +142 -0
  47. package/dist/core/format/backend-ast/utils.test.js.map +1 -0
  48. package/dist/core/format/module-emitter/assembly.d.ts +2 -2
  49. package/dist/core/format/module-emitter/assembly.d.ts.map +1 -1
  50. package/dist/core/format/module-emitter/assembly.js +7 -3
  51. package/dist/core/format/module-emitter/assembly.js.map +1 -1
  52. package/dist/core/format/module-emitter/header.d.ts +2 -1
  53. package/dist/core/format/module-emitter/header.d.ts.map +1 -1
  54. package/dist/core/format/module-emitter/header.js +2 -2
  55. package/dist/core/format/module-emitter/header.js.map +1 -1
  56. package/dist/core/format/module-emitter/orchestrator.js +1 -1
  57. package/dist/core/format/module-emitter/orchestrator.js.map +1 -1
  58. package/dist/core/format/module-emitter/static-container.d.ts.map +1 -1
  59. package/dist/core/format/module-emitter/static-container.js +3 -5
  60. package/dist/core/format/module-emitter/static-container.js.map +1 -1
  61. package/dist/core/module-emitter.test.js +1 -0
  62. package/dist/core/module-emitter.test.js.map +1 -1
  63. package/dist/core/semantic/boolean-context.d.ts.map +1 -1
  64. package/dist/core/semantic/boolean-context.js +45 -49
  65. package/dist/core/semantic/boolean-context.js.map +1 -1
  66. package/dist/core/semantic/imports.d.ts.map +1 -1
  67. package/dist/core/semantic/imports.js +25 -15
  68. package/dist/core/semantic/imports.js.map +1 -1
  69. package/dist/core/semantic/imports.test.js +43 -0
  70. package/dist/core/semantic/imports.test.js.map +1 -1
  71. package/dist/core/semantic/type-resolution.d.ts +1 -0
  72. package/dist/core/semantic/type-resolution.d.ts.map +1 -1
  73. package/dist/core/semantic/type-resolution.js +166 -5
  74. package/dist/core/semantic/type-resolution.js.map +1 -1
  75. package/dist/core/semantic/type-resolution.test.js +35 -0
  76. package/dist/core/semantic/type-resolution.test.js.map +1 -1
  77. package/dist/emitter-types/core.d.ts +24 -10
  78. package/dist/emitter-types/core.d.ts.map +1 -1
  79. package/dist/emitter.d.ts.map +1 -1
  80. package/dist/emitter.js +124 -44
  81. package/dist/emitter.js.map +1 -1
  82. package/dist/expression-emitter.d.ts.map +1 -1
  83. package/dist/expression-emitter.js +797 -17
  84. package/dist/expression-emitter.js.map +1 -1
  85. package/dist/expressions/access.d.ts.map +1 -1
  86. package/dist/expressions/access.js +65 -25
  87. package/dist/expressions/access.js.map +1 -1
  88. package/dist/expressions/calls/call-analysis.d.ts +0 -10
  89. package/dist/expressions/calls/call-analysis.d.ts.map +1 -1
  90. package/dist/expressions/calls/call-analysis.js +3 -62
  91. package/dist/expressions/calls/call-analysis.js.map +1 -1
  92. package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
  93. package/dist/expressions/calls/call-emitter.js +97 -196
  94. package/dist/expressions/calls/call-emitter.js.map +1 -1
  95. package/dist/expressions/calls/new-emitter.d.ts.map +1 -1
  96. package/dist/expressions/calls/new-emitter.js +36 -69
  97. package/dist/expressions/calls/new-emitter.js.map +1 -1
  98. package/dist/expressions/collections.d.ts +3 -0
  99. package/dist/expressions/collections.d.ts.map +1 -1
  100. package/dist/expressions/collections.js +238 -57
  101. package/dist/expressions/collections.js.map +1 -1
  102. package/dist/expressions/functions.d.ts.map +1 -1
  103. package/dist/expressions/functions.js +1 -7
  104. package/dist/expressions/functions.js.map +1 -1
  105. package/dist/expressions/identifiers.d.ts.map +1 -1
  106. package/dist/expressions/identifiers.js +24 -38
  107. package/dist/expressions/identifiers.js.map +1 -1
  108. package/dist/expressions/index.test.js +245 -0
  109. package/dist/expressions/index.test.js.map +1 -1
  110. package/dist/expressions/literals.d.ts.map +1 -1
  111. package/dist/expressions/literals.js +9 -41
  112. package/dist/expressions/literals.js.map +1 -1
  113. package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -1
  114. package/dist/expressions/operators/assignment-emitter.js +2 -6
  115. package/dist/expressions/operators/assignment-emitter.js.map +1 -1
  116. package/dist/expressions/operators/binary-emitter.d.ts.map +1 -1
  117. package/dist/expressions/operators/binary-emitter.js +102 -77
  118. package/dist/expressions/operators/binary-emitter.js.map +1 -1
  119. package/dist/expressions/operators/logical-emitter.d.ts.map +1 -1
  120. package/dist/expressions/operators/logical-emitter.js +1 -3
  121. package/dist/expressions/operators/logical-emitter.js.map +1 -1
  122. package/dist/expressions/operators/unary-emitter.d.ts.map +1 -1
  123. package/dist/expressions/operators/unary-emitter.js +9 -20
  124. package/dist/expressions/operators/unary-emitter.js.map +1 -1
  125. package/dist/expressions/other.d.ts.map +1 -1
  126. package/dist/expressions/other.js +57 -4
  127. package/dist/expressions/other.js.map +1 -1
  128. package/dist/generator-exchange.d.ts.map +1 -1
  129. package/dist/generator-exchange.js +3 -2
  130. package/dist/generator-exchange.js.map +1 -1
  131. package/dist/generator-wrapper.d.ts.map +1 -1
  132. package/dist/generator-wrapper.js +27 -56
  133. package/dist/generator-wrapper.js.map +1 -1
  134. package/dist/integration.test.js +393 -5
  135. package/dist/integration.test.js.map +1 -1
  136. package/dist/json-aot-generic.test.js +3 -0
  137. package/dist/json-aot-generic.test.js.map +1 -1
  138. package/dist/patterns.d.ts.map +1 -1
  139. package/dist/patterns.js +19 -40
  140. package/dist/patterns.js.map +1 -1
  141. package/dist/specialization/type-aliases.test.js +8 -0
  142. package/dist/specialization/type-aliases.test.js.map +1 -1
  143. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  144. package/dist/statements/classes/members/methods.js +5 -22
  145. package/dist/statements/classes/members/methods.js.map +1 -1
  146. package/dist/statements/classes/parameters.d.ts.map +1 -1
  147. package/dist/statements/classes/parameters.js +2 -1
  148. package/dist/statements/classes/parameters.js.map +1 -1
  149. package/dist/statements/classes/properties.d.ts.map +1 -1
  150. package/dist/statements/classes/properties.js +5 -16
  151. package/dist/statements/classes/properties.js.map +1 -1
  152. package/dist/statements/control/conditionals/guard-analysis.d.ts +44 -1
  153. package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -1
  154. package/dist/statements/control/conditionals/guard-analysis.js +301 -125
  155. package/dist/statements/control/conditionals/guard-analysis.js.map +1 -1
  156. package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
  157. package/dist/statements/control/conditionals/if-emitter.js +182 -53
  158. package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
  159. package/dist/statements/control/exceptions.d.ts.map +1 -1
  160. package/dist/statements/control/exceptions.js +2 -4
  161. package/dist/statements/control/exceptions.js.map +1 -1
  162. package/dist/statements/control/loops.d.ts.map +1 -1
  163. package/dist/statements/control/loops.js +3 -5
  164. package/dist/statements/control/loops.js.map +1 -1
  165. package/dist/statements/declarations/classes.d.ts.map +1 -1
  166. package/dist/statements/declarations/classes.js +2 -4
  167. package/dist/statements/declarations/classes.js.map +1 -1
  168. package/dist/statements/declarations/functions.d.ts.map +1 -1
  169. package/dist/statements/declarations/functions.js +38 -79
  170. package/dist/statements/declarations/functions.js.map +1 -1
  171. package/dist/statements/declarations/interfaces-mutable-storage.test.js +10 -2
  172. package/dist/statements/declarations/interfaces-mutable-storage.test.js.map +1 -1
  173. package/dist/statements/declarations/interfaces.d.ts.map +1 -1
  174. package/dist/statements/declarations/interfaces.js +4 -12
  175. package/dist/statements/declarations/interfaces.js.map +1 -1
  176. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  177. package/dist/statements/declarations/type-aliases.js +5 -9
  178. package/dist/statements/declarations/type-aliases.js.map +1 -1
  179. package/dist/statements/declarations/variables.d.ts.map +1 -1
  180. package/dist/statements/declarations/variables.js +55 -51
  181. package/dist/statements/declarations/variables.js.map +1 -1
  182. package/dist/statements/index.test.js +1026 -0
  183. package/dist/statements/index.test.js.map +1 -1
  184. package/dist/types/dictionaries.d.ts.map +1 -1
  185. package/dist/types/dictionaries.js +5 -5
  186. package/dist/types/dictionaries.js.map +1 -1
  187. package/dist/types/functions.d.ts.map +1 -1
  188. package/dist/types/functions.js +5 -25
  189. package/dist/types/functions.js.map +1 -1
  190. package/dist/types/primitives.d.ts.map +1 -1
  191. package/dist/types/primitives.js.map +1 -1
  192. package/dist/types/references.d.ts.map +1 -1
  193. package/dist/types/references.js +65 -128
  194. package/dist/types/references.js.map +1 -1
  195. package/dist/types/references.test.js +170 -46
  196. package/dist/types/references.test.js.map +1 -1
  197. package/dist/types/tuples.d.ts.map +1 -1
  198. package/dist/types/tuples.js +7 -17
  199. package/dist/types/tuples.js.map +1 -1
  200. package/dist/types/unions.d.ts.map +1 -1
  201. package/dist/types/unions.js +2 -5
  202. package/dist/types/unions.js.map +1 -1
  203. package/package.json +2 -2
  204. package/dist/expressions/parentheses.d.ts +0 -4
  205. package/dist/expressions/parentheses.d.ts.map +0 -1
  206. package/dist/expressions/parentheses.js +0 -91
  207. package/dist/expressions/parentheses.js.map +0 -1
@@ -610,6 +610,1032 @@ describe("Statement Emission", () => {
610
610
  expect(result).to.include("else");
611
611
  expect(result).to.include('return "negative or zero"');
612
612
  });
613
+ it("should emit instanceof guards as declaration patterns, not synthetic text expressions", () => {
614
+ const module = {
615
+ kind: "module",
616
+ filePath: "/src/test.ts",
617
+ namespace: "MyApp",
618
+ className: "test",
619
+ isStaticContainer: true,
620
+ imports: [],
621
+ body: [
622
+ {
623
+ kind: "classDeclaration",
624
+ name: "Widget",
625
+ isExported: false,
626
+ isStruct: false,
627
+ typeParameters: [],
628
+ implements: [],
629
+ members: [],
630
+ },
631
+ {
632
+ kind: "functionDeclaration",
633
+ name: "isWidget",
634
+ parameters: [
635
+ {
636
+ kind: "parameter",
637
+ pattern: { kind: "identifierPattern", name: "value" },
638
+ type: { kind: "referenceType", name: "object" },
639
+ isOptional: false,
640
+ isRest: false,
641
+ passing: "value",
642
+ },
643
+ ],
644
+ returnType: { kind: "primitiveType", name: "boolean" },
645
+ body: {
646
+ kind: "blockStatement",
647
+ statements: [
648
+ {
649
+ kind: "ifStatement",
650
+ condition: {
651
+ kind: "binary",
652
+ operator: "instanceof",
653
+ left: {
654
+ kind: "identifier",
655
+ name: "value",
656
+ inferredType: { kind: "referenceType", name: "object" },
657
+ },
658
+ right: {
659
+ kind: "identifier",
660
+ name: "Widget",
661
+ inferredType: { kind: "referenceType", name: "Widget" },
662
+ },
663
+ },
664
+ thenStatement: {
665
+ kind: "blockStatement",
666
+ statements: [
667
+ {
668
+ kind: "returnStatement",
669
+ expression: { kind: "literal", value: true },
670
+ },
671
+ ],
672
+ },
673
+ elseStatement: undefined,
674
+ },
675
+ {
676
+ kind: "returnStatement",
677
+ expression: { kind: "literal", value: false },
678
+ },
679
+ ],
680
+ },
681
+ isExported: true,
682
+ isAsync: false,
683
+ isGenerator: false,
684
+ },
685
+ ],
686
+ exports: [],
687
+ };
688
+ const result = emitModule(module);
689
+ expect(result).to.include("if (value is Widget value__is_1)");
690
+ expect(result).to.include("return true;");
691
+ expect(result).to.include("return false;");
692
+ });
693
+ it("narrows discriminated unions on truthy/falsy property guards", () => {
694
+ const okType = { kind: "referenceType", name: "Ok" };
695
+ const errType = { kind: "referenceType", name: "Err" };
696
+ const unionReference = {
697
+ kind: "referenceType",
698
+ name: "Union_2",
699
+ resolvedClrType: "global::Tsonic.Runtime.Union_2",
700
+ typeArguments: [okType, errType],
701
+ };
702
+ const unionWrapper = {
703
+ kind: "intersectionType",
704
+ types: [unionReference, { kind: "referenceType", name: "__Union$views" }],
705
+ };
706
+ const module = {
707
+ kind: "module",
708
+ filePath: "/src/test.ts",
709
+ namespace: "MyApp",
710
+ className: "test",
711
+ isStaticContainer: true,
712
+ imports: [],
713
+ body: [
714
+ {
715
+ kind: "interfaceDeclaration",
716
+ name: "Ok",
717
+ typeParameters: [],
718
+ extends: [],
719
+ members: [
720
+ {
721
+ kind: "propertySignature",
722
+ name: "success",
723
+ type: { kind: "literalType", value: true },
724
+ isOptional: false,
725
+ isReadonly: false,
726
+ },
727
+ {
728
+ kind: "propertySignature",
729
+ name: "data",
730
+ type: { kind: "primitiveType", name: "string" },
731
+ isOptional: false,
732
+ isReadonly: false,
733
+ },
734
+ ],
735
+ isExported: false,
736
+ isStruct: false,
737
+ },
738
+ {
739
+ kind: "interfaceDeclaration",
740
+ name: "Err",
741
+ typeParameters: [],
742
+ extends: [],
743
+ members: [
744
+ {
745
+ kind: "propertySignature",
746
+ name: "success",
747
+ type: { kind: "literalType", value: false },
748
+ isOptional: false,
749
+ isReadonly: false,
750
+ },
751
+ {
752
+ kind: "propertySignature",
753
+ name: "error",
754
+ type: { kind: "primitiveType", name: "string" },
755
+ isOptional: false,
756
+ isReadonly: false,
757
+ },
758
+ ],
759
+ isExported: false,
760
+ isStruct: false,
761
+ },
762
+ {
763
+ kind: "functionDeclaration",
764
+ name: "readResult",
765
+ parameters: [
766
+ {
767
+ kind: "parameter",
768
+ pattern: { kind: "identifierPattern", name: "result" },
769
+ type: unionWrapper,
770
+ isOptional: false,
771
+ isRest: false,
772
+ passing: "value",
773
+ },
774
+ ],
775
+ returnType: { kind: "primitiveType", name: "string" },
776
+ body: {
777
+ kind: "blockStatement",
778
+ statements: [
779
+ {
780
+ kind: "ifStatement",
781
+ condition: {
782
+ kind: "unary",
783
+ operator: "!",
784
+ expression: {
785
+ kind: "memberAccess",
786
+ object: {
787
+ kind: "identifier",
788
+ name: "result",
789
+ inferredType: unionWrapper,
790
+ },
791
+ property: "success",
792
+ isComputed: false,
793
+ isOptional: false,
794
+ inferredType: { kind: "literalType", value: true },
795
+ },
796
+ },
797
+ thenStatement: {
798
+ kind: "blockStatement",
799
+ statements: [
800
+ {
801
+ kind: "returnStatement",
802
+ expression: {
803
+ kind: "memberAccess",
804
+ object: {
805
+ kind: "identifier",
806
+ name: "result",
807
+ inferredType: errType,
808
+ },
809
+ property: "error",
810
+ isComputed: false,
811
+ isOptional: false,
812
+ },
813
+ },
814
+ ],
815
+ },
816
+ },
817
+ {
818
+ kind: "returnStatement",
819
+ expression: {
820
+ kind: "memberAccess",
821
+ object: {
822
+ kind: "identifier",
823
+ name: "result",
824
+ inferredType: okType,
825
+ },
826
+ property: "data",
827
+ isComputed: false,
828
+ isOptional: false,
829
+ },
830
+ },
831
+ ],
832
+ },
833
+ isExported: true,
834
+ isAsync: false,
835
+ isGenerator: false,
836
+ },
837
+ ],
838
+ exports: [],
839
+ };
840
+ const result = emitModule(module);
841
+ expect(result).to.include("if (result.Is2())");
842
+ expect(result).to.include("return result__2_1.error;");
843
+ expect(result).to.include("return (result.As1()).data;");
844
+ });
845
+ it("narrows `in`-guards for cross-module union members via the type member index", () => {
846
+ const typeMemberIndex = new Map([
847
+ [
848
+ "MyApp.Models.OkEvents",
849
+ new Map([["events", "property"]]),
850
+ ],
851
+ [
852
+ "MyApp.Models.ErrEvents",
853
+ new Map([["error", "property"]]),
854
+ ],
855
+ ]);
856
+ const unionReference = {
857
+ kind: "referenceType",
858
+ name: "Union_2",
859
+ resolvedClrType: "global::Tsonic.Runtime.Union_2",
860
+ typeArguments: [
861
+ { kind: "referenceType", name: "MyApp.Models.OkEvents" },
862
+ { kind: "referenceType", name: "MyApp.Models.ErrEvents" },
863
+ ],
864
+ };
865
+ const unionWrapper = {
866
+ kind: "intersectionType",
867
+ types: [unionReference, { kind: "referenceType", name: "__Union$views" }],
868
+ };
869
+ const module = {
870
+ kind: "module",
871
+ filePath: "/src/test.ts",
872
+ namespace: "MyApp",
873
+ className: "test",
874
+ isStaticContainer: true,
875
+ imports: [],
876
+ body: [
877
+ {
878
+ kind: "functionDeclaration",
879
+ name: "handle",
880
+ parameters: [
881
+ {
882
+ kind: "parameter",
883
+ pattern: { kind: "identifierPattern", name: "result" },
884
+ type: unionWrapper,
885
+ isOptional: false,
886
+ isRest: false,
887
+ passing: "value",
888
+ },
889
+ ],
890
+ returnType: { kind: "voidType" },
891
+ body: {
892
+ kind: "blockStatement",
893
+ statements: [
894
+ {
895
+ kind: "ifStatement",
896
+ condition: {
897
+ kind: "binary",
898
+ operator: "in",
899
+ inferredType: { kind: "primitiveType", name: "boolean" },
900
+ left: { kind: "literal", value: "error" },
901
+ right: {
902
+ kind: "identifier",
903
+ name: "result",
904
+ inferredType: unionWrapper,
905
+ },
906
+ },
907
+ thenStatement: { kind: "blockStatement", statements: [] },
908
+ elseStatement: undefined,
909
+ },
910
+ ],
911
+ },
912
+ isExported: false,
913
+ isAsync: false,
914
+ isGenerator: false,
915
+ },
916
+ ],
917
+ exports: [],
918
+ };
919
+ const result = emitModule(module, { typeMemberIndex });
920
+ expect(result).to.include("if (result.Is2())");
921
+ });
922
+ it("preserves renamed union narrowing through nullish property comparisons", () => {
923
+ const okType = { kind: "referenceType", name: "OkEvents" };
924
+ const errType = { kind: "referenceType", name: "ErrEvents" };
925
+ const unionReference = {
926
+ kind: "referenceType",
927
+ name: "Union_2",
928
+ resolvedClrType: "global::Tsonic.Runtime.Union_2",
929
+ typeArguments: [okType, errType],
930
+ };
931
+ const unionWrapper = {
932
+ kind: "intersectionType",
933
+ types: [unionReference, { kind: "referenceType", name: "__Union$views" }],
934
+ };
935
+ const module = {
936
+ kind: "module",
937
+ filePath: "/src/test.ts",
938
+ namespace: "MyApp",
939
+ className: "test",
940
+ isStaticContainer: true,
941
+ imports: [],
942
+ body: [
943
+ {
944
+ kind: "interfaceDeclaration",
945
+ name: "OkEvents",
946
+ typeParameters: [],
947
+ extends: [],
948
+ members: [
949
+ {
950
+ kind: "propertySignature",
951
+ name: "events",
952
+ type: {
953
+ kind: "arrayType",
954
+ elementType: { kind: "primitiveType", name: "string" },
955
+ },
956
+ isOptional: false,
957
+ isReadonly: false,
958
+ },
959
+ ],
960
+ isExported: false,
961
+ isStruct: false,
962
+ },
963
+ {
964
+ kind: "interfaceDeclaration",
965
+ name: "ErrEvents",
966
+ typeParameters: [],
967
+ extends: [],
968
+ members: [
969
+ {
970
+ kind: "propertySignature",
971
+ name: "error",
972
+ type: { kind: "primitiveType", name: "string" },
973
+ isOptional: false,
974
+ isReadonly: false,
975
+ },
976
+ {
977
+ kind: "propertySignature",
978
+ name: "code",
979
+ type: { kind: "primitiveType", name: "string" },
980
+ isOptional: true,
981
+ isReadonly: false,
982
+ },
983
+ ],
984
+ isExported: false,
985
+ isStruct: false,
986
+ },
987
+ {
988
+ kind: "functionDeclaration",
989
+ name: "readEvents",
990
+ parameters: [
991
+ {
992
+ kind: "parameter",
993
+ pattern: { kind: "identifierPattern", name: "result" },
994
+ type: unionWrapper,
995
+ isOptional: false,
996
+ isRest: false,
997
+ passing: "value",
998
+ },
999
+ ],
1000
+ returnType: { kind: "primitiveType", name: "string" },
1001
+ body: {
1002
+ kind: "blockStatement",
1003
+ statements: [
1004
+ {
1005
+ kind: "ifStatement",
1006
+ condition: {
1007
+ kind: "binary",
1008
+ operator: "in",
1009
+ left: { kind: "literal", value: "error" },
1010
+ right: {
1011
+ kind: "identifier",
1012
+ name: "result",
1013
+ inferredType: unionWrapper,
1014
+ },
1015
+ },
1016
+ thenStatement: {
1017
+ kind: "blockStatement",
1018
+ statements: [
1019
+ {
1020
+ kind: "returnStatement",
1021
+ expression: {
1022
+ kind: "conditional",
1023
+ condition: {
1024
+ kind: "binary",
1025
+ operator: "===",
1026
+ left: {
1027
+ kind: "memberAccess",
1028
+ object: {
1029
+ kind: "identifier",
1030
+ name: "result",
1031
+ inferredType: errType,
1032
+ },
1033
+ property: "code",
1034
+ isComputed: false,
1035
+ isOptional: false,
1036
+ inferredType: {
1037
+ kind: "unionType",
1038
+ types: [
1039
+ { kind: "primitiveType", name: "string" },
1040
+ { kind: "primitiveType", name: "undefined" },
1041
+ ],
1042
+ },
1043
+ },
1044
+ right: { kind: "identifier", name: "undefined" },
1045
+ },
1046
+ whenTrue: {
1047
+ kind: "memberAccess",
1048
+ object: {
1049
+ kind: "identifier",
1050
+ name: "result",
1051
+ inferredType: errType,
1052
+ },
1053
+ property: "error",
1054
+ isComputed: false,
1055
+ isOptional: false,
1056
+ },
1057
+ whenFalse: {
1058
+ kind: "binary",
1059
+ operator: "+",
1060
+ left: {
1061
+ kind: "binary",
1062
+ operator: "+",
1063
+ left: {
1064
+ kind: "memberAccess",
1065
+ object: {
1066
+ kind: "identifier",
1067
+ name: "result",
1068
+ inferredType: errType,
1069
+ },
1070
+ property: "code",
1071
+ isComputed: false,
1072
+ isOptional: false,
1073
+ inferredType: {
1074
+ kind: "unionType",
1075
+ types: [
1076
+ { kind: "primitiveType", name: "string" },
1077
+ { kind: "primitiveType", name: "undefined" },
1078
+ ],
1079
+ },
1080
+ },
1081
+ right: { kind: "literal", value: ":" },
1082
+ },
1083
+ right: {
1084
+ kind: "memberAccess",
1085
+ object: {
1086
+ kind: "identifier",
1087
+ name: "result",
1088
+ inferredType: errType,
1089
+ },
1090
+ property: "error",
1091
+ isComputed: false,
1092
+ isOptional: false,
1093
+ },
1094
+ },
1095
+ },
1096
+ },
1097
+ ],
1098
+ },
1099
+ },
1100
+ {
1101
+ kind: "returnStatement",
1102
+ expression: { kind: "literal", value: "" },
1103
+ },
1104
+ ],
1105
+ },
1106
+ isExported: true,
1107
+ isAsync: false,
1108
+ isGenerator: false,
1109
+ },
1110
+ ],
1111
+ exports: [],
1112
+ };
1113
+ const result = emitModule(module);
1114
+ expect(result).to.include("if (result.Is2())");
1115
+ expect(result).to.include('return result__2_1.code == null ? result__2_1.error : result__2_1.code + ":" + result__2_1.error;');
1116
+ expect(result).to.not.include("return result.code == null");
1117
+ });
1118
+ it("handles `in` guards after earlier narrowing collapses a union to one member", () => {
1119
+ const module = {
1120
+ kind: "module",
1121
+ filePath: "/src/test.ts",
1122
+ namespace: "MyApp",
1123
+ className: "test",
1124
+ isStaticContainer: true,
1125
+ imports: [],
1126
+ body: [
1127
+ {
1128
+ kind: "interfaceDeclaration",
1129
+ name: "Shape__0",
1130
+ typeParameters: [],
1131
+ extends: [],
1132
+ members: [
1133
+ {
1134
+ kind: "propertySignature",
1135
+ name: "a",
1136
+ type: { kind: "primitiveType", name: "string" },
1137
+ isOptional: false,
1138
+ isReadonly: false,
1139
+ },
1140
+ ],
1141
+ isExported: false,
1142
+ isStruct: false,
1143
+ },
1144
+ {
1145
+ kind: "interfaceDeclaration",
1146
+ name: "Shape__1",
1147
+ typeParameters: [],
1148
+ extends: [],
1149
+ members: [
1150
+ {
1151
+ kind: "propertySignature",
1152
+ name: "b",
1153
+ type: { kind: "primitiveType", name: "number" },
1154
+ isOptional: false,
1155
+ isReadonly: false,
1156
+ },
1157
+ ],
1158
+ isExported: false,
1159
+ isStruct: false,
1160
+ },
1161
+ {
1162
+ kind: "interfaceDeclaration",
1163
+ name: "Shape__2",
1164
+ typeParameters: [],
1165
+ extends: [],
1166
+ members: [
1167
+ {
1168
+ kind: "propertySignature",
1169
+ name: "c",
1170
+ type: { kind: "primitiveType", name: "boolean" },
1171
+ isOptional: false,
1172
+ isReadonly: false,
1173
+ },
1174
+ ],
1175
+ isExported: false,
1176
+ isStruct: false,
1177
+ },
1178
+ {
1179
+ kind: "typeAliasDeclaration",
1180
+ name: "Shape",
1181
+ typeParameters: [],
1182
+ type: {
1183
+ kind: "unionType",
1184
+ types: [
1185
+ { kind: "referenceType", name: "Shape__0" },
1186
+ { kind: "referenceType", name: "Shape__1" },
1187
+ { kind: "referenceType", name: "Shape__2" },
1188
+ ],
1189
+ },
1190
+ isExported: false,
1191
+ isStruct: false,
1192
+ },
1193
+ {
1194
+ kind: "functionDeclaration",
1195
+ name: "fmt",
1196
+ parameters: [
1197
+ {
1198
+ kind: "parameter",
1199
+ pattern: { kind: "identifierPattern", name: "s" },
1200
+ type: { kind: "referenceType", name: "Shape" },
1201
+ isOptional: false,
1202
+ isRest: false,
1203
+ passing: "value",
1204
+ },
1205
+ ],
1206
+ returnType: { kind: "primitiveType", name: "string" },
1207
+ body: {
1208
+ kind: "blockStatement",
1209
+ statements: [
1210
+ {
1211
+ kind: "ifStatement",
1212
+ condition: {
1213
+ kind: "binary",
1214
+ operator: "in",
1215
+ left: { kind: "literal", value: "a", raw: '"a"' },
1216
+ right: {
1217
+ kind: "identifier",
1218
+ name: "s",
1219
+ inferredType: { kind: "referenceType", name: "Shape" },
1220
+ },
1221
+ },
1222
+ thenStatement: {
1223
+ kind: "blockStatement",
1224
+ statements: [
1225
+ {
1226
+ kind: "returnStatement",
1227
+ expression: { kind: "literal", value: "A" },
1228
+ },
1229
+ ],
1230
+ },
1231
+ },
1232
+ {
1233
+ kind: "ifStatement",
1234
+ condition: {
1235
+ kind: "binary",
1236
+ operator: "in",
1237
+ left: { kind: "literal", value: "b", raw: '"b"' },
1238
+ right: {
1239
+ kind: "identifier",
1240
+ name: "s",
1241
+ inferredType: {
1242
+ kind: "unionType",
1243
+ types: [
1244
+ { kind: "referenceType", name: "Shape__1" },
1245
+ { kind: "referenceType", name: "Shape__2" },
1246
+ ],
1247
+ },
1248
+ },
1249
+ },
1250
+ thenStatement: {
1251
+ kind: "blockStatement",
1252
+ statements: [
1253
+ {
1254
+ kind: "returnStatement",
1255
+ expression: { kind: "literal", value: "B" },
1256
+ },
1257
+ ],
1258
+ },
1259
+ },
1260
+ {
1261
+ kind: "ifStatement",
1262
+ condition: {
1263
+ kind: "binary",
1264
+ operator: "in",
1265
+ left: { kind: "literal", value: "c", raw: '"c"' },
1266
+ right: {
1267
+ kind: "identifier",
1268
+ name: "s",
1269
+ inferredType: { kind: "referenceType", name: "Shape__2" },
1270
+ },
1271
+ },
1272
+ thenStatement: {
1273
+ kind: "blockStatement",
1274
+ statements: [
1275
+ {
1276
+ kind: "returnStatement",
1277
+ expression: { kind: "literal", value: "C" },
1278
+ },
1279
+ ],
1280
+ },
1281
+ },
1282
+ {
1283
+ kind: "returnStatement",
1284
+ expression: { kind: "literal", value: "unreachable" },
1285
+ },
1286
+ ],
1287
+ },
1288
+ isExported: false,
1289
+ isAsync: false,
1290
+ isGenerator: false,
1291
+ },
1292
+ ],
1293
+ exports: [],
1294
+ };
1295
+ const result = emitModule(module);
1296
+ expect(result).to.include("if (s.Is1())");
1297
+ expect(result).to.include("if (s.Is2())");
1298
+ expect(result).to.include("if (true)");
1299
+ });
1300
+ it("maps discriminant guards to original runtime union members after earlier narrowing", () => {
1301
+ const module = {
1302
+ kind: "module",
1303
+ filePath: "/src/test.ts",
1304
+ namespace: "MyApp",
1305
+ className: "test",
1306
+ isStaticContainer: true,
1307
+ imports: [],
1308
+ body: [
1309
+ {
1310
+ kind: "interfaceDeclaration",
1311
+ name: "Shape__0",
1312
+ typeParameters: [],
1313
+ extends: [],
1314
+ members: [
1315
+ {
1316
+ kind: "propertySignature",
1317
+ name: "kind",
1318
+ type: { kind: "literalType", value: "a" },
1319
+ isOptional: false,
1320
+ isReadonly: false,
1321
+ },
1322
+ ],
1323
+ isExported: false,
1324
+ isStruct: false,
1325
+ },
1326
+ {
1327
+ kind: "interfaceDeclaration",
1328
+ name: "Shape__1",
1329
+ typeParameters: [],
1330
+ extends: [],
1331
+ members: [
1332
+ {
1333
+ kind: "propertySignature",
1334
+ name: "kind",
1335
+ type: { kind: "literalType", value: "b" },
1336
+ isOptional: false,
1337
+ isReadonly: false,
1338
+ },
1339
+ ],
1340
+ isExported: false,
1341
+ isStruct: false,
1342
+ },
1343
+ {
1344
+ kind: "interfaceDeclaration",
1345
+ name: "Shape__2",
1346
+ typeParameters: [],
1347
+ extends: [],
1348
+ members: [
1349
+ {
1350
+ kind: "propertySignature",
1351
+ name: "kind",
1352
+ type: { kind: "literalType", value: "c" },
1353
+ isOptional: false,
1354
+ isReadonly: false,
1355
+ },
1356
+ ],
1357
+ isExported: false,
1358
+ isStruct: false,
1359
+ },
1360
+ {
1361
+ kind: "typeAliasDeclaration",
1362
+ name: "Shape",
1363
+ typeParameters: [],
1364
+ type: {
1365
+ kind: "unionType",
1366
+ types: [
1367
+ { kind: "referenceType", name: "Shape__0" },
1368
+ { kind: "referenceType", name: "Shape__1" },
1369
+ { kind: "referenceType", name: "Shape__2" },
1370
+ ],
1371
+ },
1372
+ isExported: false,
1373
+ isStruct: false,
1374
+ },
1375
+ {
1376
+ kind: "functionDeclaration",
1377
+ name: "fmt",
1378
+ parameters: [
1379
+ {
1380
+ kind: "parameter",
1381
+ pattern: { kind: "identifierPattern", name: "s" },
1382
+ type: { kind: "referenceType", name: "Shape" },
1383
+ isOptional: false,
1384
+ isRest: false,
1385
+ passing: "value",
1386
+ },
1387
+ ],
1388
+ returnType: { kind: "primitiveType", name: "string" },
1389
+ body: {
1390
+ kind: "blockStatement",
1391
+ statements: [
1392
+ {
1393
+ kind: "ifStatement",
1394
+ condition: {
1395
+ kind: "binary",
1396
+ operator: "===",
1397
+ left: {
1398
+ kind: "memberAccess",
1399
+ object: {
1400
+ kind: "identifier",
1401
+ name: "s",
1402
+ inferredType: { kind: "referenceType", name: "Shape" },
1403
+ },
1404
+ property: "kind",
1405
+ isComputed: false,
1406
+ isOptional: false,
1407
+ },
1408
+ right: { kind: "literal", value: "a", raw: '"a"' },
1409
+ },
1410
+ thenStatement: {
1411
+ kind: "blockStatement",
1412
+ statements: [
1413
+ {
1414
+ kind: "returnStatement",
1415
+ expression: { kind: "literal", value: "A" },
1416
+ },
1417
+ ],
1418
+ },
1419
+ },
1420
+ {
1421
+ kind: "ifStatement",
1422
+ condition: {
1423
+ kind: "binary",
1424
+ operator: "===",
1425
+ left: {
1426
+ kind: "memberAccess",
1427
+ object: {
1428
+ kind: "identifier",
1429
+ name: "s",
1430
+ inferredType: {
1431
+ kind: "unionType",
1432
+ types: [
1433
+ { kind: "referenceType", name: "Shape__1" },
1434
+ { kind: "referenceType", name: "Shape__2" },
1435
+ ],
1436
+ },
1437
+ },
1438
+ property: "kind",
1439
+ isComputed: false,
1440
+ isOptional: false,
1441
+ },
1442
+ right: { kind: "literal", value: "b", raw: '"b"' },
1443
+ },
1444
+ thenStatement: {
1445
+ kind: "blockStatement",
1446
+ statements: [
1447
+ {
1448
+ kind: "returnStatement",
1449
+ expression: { kind: "literal", value: "B" },
1450
+ },
1451
+ ],
1452
+ },
1453
+ },
1454
+ {
1455
+ kind: "ifStatement",
1456
+ condition: {
1457
+ kind: "binary",
1458
+ operator: "===",
1459
+ left: {
1460
+ kind: "memberAccess",
1461
+ object: {
1462
+ kind: "identifier",
1463
+ name: "s",
1464
+ inferredType: { kind: "referenceType", name: "Shape__2" },
1465
+ },
1466
+ property: "kind",
1467
+ isComputed: false,
1468
+ isOptional: false,
1469
+ },
1470
+ right: { kind: "literal", value: "c", raw: '"c"' },
1471
+ },
1472
+ thenStatement: {
1473
+ kind: "blockStatement",
1474
+ statements: [
1475
+ {
1476
+ kind: "returnStatement",
1477
+ expression: { kind: "literal", value: "C" },
1478
+ },
1479
+ ],
1480
+ },
1481
+ },
1482
+ ],
1483
+ },
1484
+ isExported: false,
1485
+ isAsync: false,
1486
+ isGenerator: false,
1487
+ },
1488
+ ],
1489
+ exports: [],
1490
+ };
1491
+ const result = emitModule(module);
1492
+ expect(result).to.include("if (s.Is1())");
1493
+ expect(result).to.include("if (s.Is2())");
1494
+ expect(result).to.include('if ((s.As3()).kind == "c")');
1495
+ });
1496
+ it("maps predicate guards to original runtime union members after earlier narrowing", () => {
1497
+ const shape0 = { kind: "referenceType", name: "Shape__0" };
1498
+ const shape1 = { kind: "referenceType", name: "Shape__1" };
1499
+ const shape2 = { kind: "referenceType", name: "Shape__2" };
1500
+ const module = {
1501
+ kind: "module",
1502
+ filePath: "/src/test.ts",
1503
+ namespace: "MyApp",
1504
+ className: "test",
1505
+ isStaticContainer: true,
1506
+ imports: [],
1507
+ body: [
1508
+ {
1509
+ kind: "interfaceDeclaration",
1510
+ name: "Shape__0",
1511
+ typeParameters: [],
1512
+ extends: [],
1513
+ members: [],
1514
+ isExported: false,
1515
+ isStruct: false,
1516
+ },
1517
+ {
1518
+ kind: "interfaceDeclaration",
1519
+ name: "Shape__1",
1520
+ typeParameters: [],
1521
+ extends: [],
1522
+ members: [],
1523
+ isExported: false,
1524
+ isStruct: false,
1525
+ },
1526
+ {
1527
+ kind: "interfaceDeclaration",
1528
+ name: "Shape__2",
1529
+ typeParameters: [],
1530
+ extends: [],
1531
+ members: [],
1532
+ isExported: false,
1533
+ isStruct: false,
1534
+ },
1535
+ {
1536
+ kind: "typeAliasDeclaration",
1537
+ name: "Shape",
1538
+ typeParameters: [],
1539
+ type: {
1540
+ kind: "unionType",
1541
+ types: [shape0, shape1, shape2],
1542
+ },
1543
+ isExported: false,
1544
+ isStruct: false,
1545
+ },
1546
+ {
1547
+ kind: "functionDeclaration",
1548
+ name: "fmt",
1549
+ parameters: [
1550
+ {
1551
+ kind: "parameter",
1552
+ pattern: { kind: "identifierPattern", name: "s" },
1553
+ type: { kind: "referenceType", name: "Shape" },
1554
+ isOptional: false,
1555
+ isRest: false,
1556
+ passing: "value",
1557
+ },
1558
+ ],
1559
+ returnType: { kind: "primitiveType", name: "string" },
1560
+ body: {
1561
+ kind: "blockStatement",
1562
+ statements: [
1563
+ {
1564
+ kind: "ifStatement",
1565
+ condition: {
1566
+ kind: "call",
1567
+ callee: { kind: "identifier", name: "isA" },
1568
+ arguments: [
1569
+ {
1570
+ kind: "identifier",
1571
+ name: "s",
1572
+ inferredType: { kind: "referenceType", name: "Shape" },
1573
+ },
1574
+ ],
1575
+ isOptional: false,
1576
+ inferredType: { kind: "primitiveType", name: "boolean" },
1577
+ narrowing: {
1578
+ kind: "typePredicate",
1579
+ argIndex: 0,
1580
+ targetType: shape0,
1581
+ },
1582
+ },
1583
+ thenStatement: {
1584
+ kind: "blockStatement",
1585
+ statements: [
1586
+ {
1587
+ kind: "returnStatement",
1588
+ expression: { kind: "literal", value: "A" },
1589
+ },
1590
+ ],
1591
+ },
1592
+ },
1593
+ {
1594
+ kind: "ifStatement",
1595
+ condition: {
1596
+ kind: "call",
1597
+ callee: { kind: "identifier", name: "isB" },
1598
+ arguments: [
1599
+ {
1600
+ kind: "identifier",
1601
+ name: "s",
1602
+ inferredType: {
1603
+ kind: "unionType",
1604
+ types: [shape1, shape2],
1605
+ },
1606
+ },
1607
+ ],
1608
+ isOptional: false,
1609
+ inferredType: { kind: "primitiveType", name: "boolean" },
1610
+ narrowing: {
1611
+ kind: "typePredicate",
1612
+ argIndex: 0,
1613
+ targetType: shape1,
1614
+ },
1615
+ },
1616
+ thenStatement: {
1617
+ kind: "blockStatement",
1618
+ statements: [
1619
+ {
1620
+ kind: "returnStatement",
1621
+ expression: { kind: "literal", value: "B" },
1622
+ },
1623
+ ],
1624
+ },
1625
+ },
1626
+ ],
1627
+ },
1628
+ isExported: false,
1629
+ isAsync: false,
1630
+ isGenerator: false,
1631
+ },
1632
+ ],
1633
+ exports: [],
1634
+ };
1635
+ const result = emitModule(module);
1636
+ expect(result).to.include("if (s.Is1())");
1637
+ expect(result).to.include("if (s.Is2())");
1638
+ });
613
1639
  it("should emit canonical for loops with int counter and no cast", () => {
614
1640
  // Test: for (let i = 0; i < list.Count; i++) { list[i] }
615
1641
  // Should emit: for (int i = 0; ...) { list[i] } - NO (int) cast