@tsonic/emitter 0.0.71 → 0.0.73

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