@tsonic/frontend 0.0.67 → 0.0.68

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 (200) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/generic-function-values.d.ts.map +1 -1
  3. package/dist/generic-function-values.js +4 -2
  4. package/dist/generic-function-values.js.map +1 -1
  5. package/dist/graph/extraction/imports.d.ts.map +1 -1
  6. package/dist/graph/extraction/imports.js +5 -1
  7. package/dist/graph/extraction/imports.js.map +1 -1
  8. package/dist/ir/binding/binding-factory.d.ts.map +1 -1
  9. package/dist/ir/binding/binding-factory.js +239 -26
  10. package/dist/ir/binding/binding-factory.js.map +1 -1
  11. package/dist/ir/binding/binding-types.d.ts +1 -0
  12. package/dist/ir/binding/binding-types.d.ts.map +1 -1
  13. package/dist/ir/binding-resolution.test.js +171 -0
  14. package/dist/ir/binding-resolution.test.js.map +1 -1
  15. package/dist/ir/builder/imports.d.ts.map +1 -1
  16. package/dist/ir/builder/imports.js +13 -1
  17. package/dist/ir/builder/imports.js.map +1 -1
  18. package/dist/ir/builder.test.js +1347 -4
  19. package/dist/ir/builder.test.js.map +1 -1
  20. package/dist/ir/converters/anonymous-synthesis.d.ts +3 -2
  21. package/dist/ir/converters/anonymous-synthesis.d.ts.map +1 -1
  22. package/dist/ir/converters/anonymous-synthesis.js +88 -67
  23. package/dist/ir/converters/anonymous-synthesis.js.map +1 -1
  24. package/dist/ir/converters/expressions/access/access-converter.d.ts.map +1 -1
  25. package/dist/ir/converters/expressions/access/access-converter.js +44 -36
  26. package/dist/ir/converters/expressions/access/access-converter.js.map +1 -1
  27. package/dist/ir/converters/expressions/access/binding-resolution.d.ts.map +1 -1
  28. package/dist/ir/converters/expressions/access/binding-resolution.js.map +1 -1
  29. package/dist/ir/converters/expressions/access/member-resolution.d.ts.map +1 -1
  30. package/dist/ir/converters/expressions/access/member-resolution.js +15 -3
  31. package/dist/ir/converters/expressions/access/member-resolution.js.map +1 -1
  32. package/dist/ir/converters/expressions/calls/call-converter.d.ts +2 -2
  33. package/dist/ir/converters/expressions/calls/call-converter.d.ts.map +1 -1
  34. package/dist/ir/converters/expressions/calls/call-converter.js +108 -4
  35. package/dist/ir/converters/expressions/calls/call-converter.js.map +1 -1
  36. package/dist/ir/converters/expressions/calls/new-converter.d.ts +2 -1
  37. package/dist/ir/converters/expressions/calls/new-converter.d.ts.map +1 -1
  38. package/dist/ir/converters/expressions/calls/new-converter.js +4 -1
  39. package/dist/ir/converters/expressions/calls/new-converter.js.map +1 -1
  40. package/dist/ir/converters/expressions/collections.d.ts.map +1 -1
  41. package/dist/ir/converters/expressions/collections.js +425 -121
  42. package/dist/ir/converters/expressions/collections.js.map +1 -1
  43. package/dist/ir/converters/expressions/dynamic-import.d.ts +6 -0
  44. package/dist/ir/converters/expressions/dynamic-import.d.ts.map +1 -0
  45. package/dist/ir/converters/expressions/dynamic-import.js +121 -0
  46. package/dist/ir/converters/expressions/dynamic-import.js.map +1 -0
  47. package/dist/ir/converters/expressions/functions.d.ts.map +1 -1
  48. package/dist/ir/converters/expressions/functions.js +28 -13
  49. package/dist/ir/converters/expressions/functions.js.map +1 -1
  50. package/dist/ir/converters/expressions/import-meta.d.ts +9 -0
  51. package/dist/ir/converters/expressions/import-meta.d.ts.map +1 -0
  52. package/dist/ir/converters/expressions/import-meta.js +93 -0
  53. package/dist/ir/converters/expressions/import-meta.js.map +1 -0
  54. package/dist/ir/converters/expressions/literals.d.ts +2 -1
  55. package/dist/ir/converters/expressions/literals.d.ts.map +1 -1
  56. package/dist/ir/converters/expressions/literals.js +73 -0
  57. package/dist/ir/converters/expressions/literals.js.map +1 -1
  58. package/dist/ir/converters/flow-narrowing.d.ts.map +1 -1
  59. package/dist/ir/converters/flow-narrowing.js +100 -0
  60. package/dist/ir/converters/flow-narrowing.js.map +1 -1
  61. package/dist/ir/converters/statements/control/loops.d.ts.map +1 -1
  62. package/dist/ir/converters/statements/control/loops.js +72 -8
  63. package/dist/ir/converters/statements/control/loops.js.map +1 -1
  64. package/dist/ir/converters/statements/declarations/variables.d.ts.map +1 -1
  65. package/dist/ir/converters/statements/declarations/variables.js +22 -6
  66. package/dist/ir/converters/statements/declarations/variables.js.map +1 -1
  67. package/dist/ir/expression-converter.d.ts.map +1 -1
  68. package/dist/ir/expression-converter.js +13 -4
  69. package/dist/ir/expression-converter.js.map +1 -1
  70. package/dist/ir/program-context.d.ts +27 -0
  71. package/dist/ir/program-context.d.ts.map +1 -1
  72. package/dist/ir/program-context.js +12 -0
  73. package/dist/ir/program-context.js.map +1 -1
  74. package/dist/ir/type-system/internal/handle-types.d.ts +1 -0
  75. package/dist/ir/type-system/internal/handle-types.d.ts.map +1 -1
  76. package/dist/ir/type-system/internal/type-converter/references.d.ts.map +1 -1
  77. package/dist/ir/type-system/internal/type-converter/references.js +37 -1
  78. package/dist/ir/type-system/internal/type-converter/references.js.map +1 -1
  79. package/dist/ir/type-system/type-system-call-resolution.d.ts +1 -1
  80. package/dist/ir/type-system/type-system-call-resolution.d.ts.map +1 -1
  81. package/dist/ir/type-system/type-system-call-resolution.js +158 -26
  82. package/dist/ir/type-system/type-system-call-resolution.js.map +1 -1
  83. package/dist/ir/type-system/type-system-inference.d.ts +1 -1
  84. package/dist/ir/type-system/type-system-inference.d.ts.map +1 -1
  85. package/dist/ir/type-system/type-system-inference.js +189 -31
  86. package/dist/ir/type-system/type-system-inference.js.map +1 -1
  87. package/dist/ir/type-system/type-system-state.d.ts +14 -0
  88. package/dist/ir/type-system/type-system-state.d.ts.map +1 -1
  89. package/dist/ir/type-system/type-system-state.js +1 -0
  90. package/dist/ir/type-system/type-system-state.js.map +1 -1
  91. package/dist/ir/type-system/type-system.d.ts +5 -1
  92. package/dist/ir/type-system/type-system.d.ts.map +1 -1
  93. package/dist/ir/type-system/type-system.js +5 -2
  94. package/dist/ir/type-system/type-system.js.map +1 -1
  95. package/dist/ir/types/expressions.d.ts +55 -1
  96. package/dist/ir/types/expressions.d.ts.map +1 -1
  97. package/dist/ir/types/index.d.ts +1 -1
  98. package/dist/ir/types/index.d.ts.map +1 -1
  99. package/dist/ir/types/index.js.map +1 -1
  100. package/dist/ir/types/module.d.ts +2 -0
  101. package/dist/ir/types/module.d.ts.map +1 -1
  102. package/dist/ir/types/type-ops.d.ts.map +1 -1
  103. package/dist/ir/types/type-ops.js +1 -0
  104. package/dist/ir/types/type-ops.js.map +1 -1
  105. package/dist/ir/types.d.ts +1 -1
  106. package/dist/ir/types.d.ts.map +1 -1
  107. package/dist/ir/types.js.map +1 -1
  108. package/dist/ir/validation/anonymous-type-lowering-pass.d.ts.map +1 -1
  109. package/dist/ir/validation/anonymous-type-lowering-pass.js +97 -9
  110. package/dist/ir/validation/anonymous-type-lowering-pass.js.map +1 -1
  111. package/dist/ir/validation/arrow-return-finalization-pass.js +3 -0
  112. package/dist/ir/validation/arrow-return-finalization-pass.js.map +1 -1
  113. package/dist/ir/validation/char-validation-pass.d.ts.map +1 -1
  114. package/dist/ir/validation/char-validation-pass.js +10 -0
  115. package/dist/ir/validation/char-validation-pass.js.map +1 -1
  116. package/dist/ir/validation/numeric-coercion-pass.d.ts.map +1 -1
  117. package/dist/ir/validation/numeric-coercion-pass.js +6 -0
  118. package/dist/ir/validation/numeric-coercion-pass.js.map +1 -1
  119. package/dist/ir/validation/numeric-invariants.test.js +180 -0
  120. package/dist/ir/validation/numeric-invariants.test.js.map +1 -1
  121. package/dist/ir/validation/numeric-proof-pass.d.ts.map +1 -1
  122. package/dist/ir/validation/numeric-proof-pass.js +188 -2
  123. package/dist/ir/validation/numeric-proof-pass.js.map +1 -1
  124. package/dist/ir/validation/soundness-gate.d.ts.map +1 -1
  125. package/dist/ir/validation/soundness-gate.js +29 -10
  126. package/dist/ir/validation/soundness-gate.js.map +1 -1
  127. package/dist/ir/validation/soundness-gate.test.js +127 -4
  128. package/dist/ir/validation/soundness-gate.test.js.map +1 -1
  129. package/dist/ir/validation/yield-lowering-pass.d.ts.map +1 -1
  130. package/dist/ir/validation/yield-lowering-pass.js +26 -2
  131. package/dist/ir/validation/yield-lowering-pass.js.map +1 -1
  132. package/dist/object-literal-method-runtime.d.ts +15 -0
  133. package/dist/object-literal-method-runtime.d.ts.map +1 -0
  134. package/dist/object-literal-method-runtime.js +279 -0
  135. package/dist/object-literal-method-runtime.js.map +1 -0
  136. package/dist/program/bindings.test.js +44 -0
  137. package/dist/program/bindings.test.js.map +1 -1
  138. package/dist/program/creation.d.ts.map +1 -1
  139. package/dist/program/creation.js +141 -18
  140. package/dist/program/creation.js.map +1 -1
  141. package/dist/program/creation.test.js +312 -2
  142. package/dist/program/creation.test.js.map +1 -1
  143. package/dist/program/dependency-graph.d.ts.map +1 -1
  144. package/dist/program/dependency-graph.js +49 -22
  145. package/dist/program/dependency-graph.js.map +1 -1
  146. package/dist/program/dependency-graph.test.d.ts +2 -0
  147. package/dist/program/dependency-graph.test.d.ts.map +1 -0
  148. package/dist/program/dependency-graph.test.js +118 -0
  149. package/dist/program/dependency-graph.test.js.map +1 -0
  150. package/dist/resolver/dynamic-import.d.ts +33 -0
  151. package/dist/resolver/dynamic-import.d.ts.map +1 -0
  152. package/dist/resolver/dynamic-import.js +142 -0
  153. package/dist/resolver/dynamic-import.js.map +1 -0
  154. package/dist/resolver/dynamic-import.test.d.ts +2 -0
  155. package/dist/resolver/dynamic-import.test.d.ts.map +1 -0
  156. package/dist/resolver/dynamic-import.test.js +150 -0
  157. package/dist/resolver/dynamic-import.test.js.map +1 -0
  158. package/dist/resolver/import-resolution.d.ts +2 -0
  159. package/dist/resolver/import-resolution.d.ts.map +1 -1
  160. package/dist/resolver/import-resolution.js +20 -3
  161. package/dist/resolver/import-resolution.js.map +1 -1
  162. package/dist/resolver/source-package-resolution.d.ts +11 -0
  163. package/dist/resolver/source-package-resolution.d.ts.map +1 -0
  164. package/dist/resolver/source-package-resolution.js +188 -0
  165. package/dist/resolver/source-package-resolution.js.map +1 -0
  166. package/dist/resolver/source-package-resolution.test.d.ts +2 -0
  167. package/dist/resolver/source-package-resolution.test.d.ts.map +1 -0
  168. package/dist/resolver/source-package-resolution.test.js +77 -0
  169. package/dist/resolver/source-package-resolution.test.js.map +1 -0
  170. package/dist/resolver/types.d.ts +1 -0
  171. package/dist/resolver/types.d.ts.map +1 -1
  172. package/dist/resolver.test.js +83 -0
  173. package/dist/resolver.test.js.map +1 -1
  174. package/dist/surface/profiles.d.ts +1 -0
  175. package/dist/surface/profiles.d.ts.map +1 -1
  176. package/dist/surface/profiles.js +4 -2
  177. package/dist/surface/profiles.js.map +1 -1
  178. package/dist/surface/profiles.test.js +10 -0
  179. package/dist/surface/profiles.test.js.map +1 -1
  180. package/dist/types/module.d.ts +1 -1
  181. package/dist/types/module.d.ts.map +1 -1
  182. package/dist/validation/features.d.ts +1 -1
  183. package/dist/validation/features.d.ts.map +1 -1
  184. package/dist/validation/features.js +37 -6
  185. package/dist/validation/features.js.map +1 -1
  186. package/dist/validation/features.test.js +137 -23
  187. package/dist/validation/features.test.js.map +1 -1
  188. package/dist/validation/imports.d.ts.map +1 -1
  189. package/dist/validation/imports.js +2 -8
  190. package/dist/validation/imports.js.map +1 -1
  191. package/dist/validation/imports.test.js +6 -9
  192. package/dist/validation/imports.test.js.map +1 -1
  193. package/dist/validation/static-safety.d.ts.map +1 -1
  194. package/dist/validation/static-safety.js +118 -67
  195. package/dist/validation/static-safety.js.map +1 -1
  196. package/dist/validator.maximus.test.js +225 -33
  197. package/dist/validator.maximus.test.js.map +1 -1
  198. package/dist/validator.test.js +87 -6
  199. package/dist/validator.test.js.map +1 -1
  200. package/package.json +1 -1
@@ -2,11 +2,14 @@
2
2
  * Collection expression converters (arrays and objects)
3
3
  */
4
4
  import * as ts from "typescript";
5
- import { typesEqual } from "../../types/ir-substitution.js";
5
+ import { containsTypeParameter, typesEqual, } from "../../types/ir-substitution.js";
6
6
  import { getSourceSpan, getContextualType } from "./helpers.js";
7
7
  import { convertExpression } from "../../expression-converter.js";
8
- import { checkSynthesisEligibility, } from "../anonymous-synthesis.js";
8
+ import { checkSynthesisEligibility } from "../anonymous-synthesis.js";
9
9
  import { createDiagnostic } from "../../../types/diagnostic.js";
10
+ import { convertAccessorProperty } from "../statements/declarations/classes/properties.js";
11
+ import { convertBindingName } from "../../syntax/binding-patterns.js";
12
+ import { createObjectLiteralMethodArgumentPrelude } from "../../../object-literal-method-runtime.js";
10
13
  /**
11
14
  * Compute the element type for an array literal from its elements' types.
12
15
  *
@@ -19,9 +22,78 @@ import { createDiagnostic } from "../../../types/diagnostic.js";
19
22
  * 6. Mixed or complex → fall back to TS inference
20
23
  */
21
24
  const computeArrayElementType = (elements, fallbackType) => {
25
+ const mergeElementTypes = (types) => {
26
+ if (types.length === 0)
27
+ return undefined;
28
+ const first = types[0];
29
+ if (first && types.every((t) => typesEqual(t, first))) {
30
+ return first;
31
+ }
32
+ return {
33
+ kind: "unionType",
34
+ types,
35
+ };
36
+ };
37
+ const extractSpreadElementTypes = (type) => {
38
+ if (!type)
39
+ return undefined;
40
+ if (type.kind === "arrayType") {
41
+ return [type.elementType];
42
+ }
43
+ if (type.kind === "tupleType") {
44
+ return type.elementTypes.filter((element) => element !== undefined);
45
+ }
46
+ if (type.kind === "unionType") {
47
+ const members = [];
48
+ for (const member of type.types) {
49
+ const extracted = extractSpreadElementTypes(member);
50
+ if (!extracted)
51
+ return undefined;
52
+ members.push(...extracted);
53
+ }
54
+ return members;
55
+ }
56
+ if (type.kind === "referenceType" &&
57
+ type.typeArguments &&
58
+ type.typeArguments.length > 0) {
59
+ const simpleName = type.name.split(".").pop() ?? type.name;
60
+ switch (simpleName) {
61
+ case "Array":
62
+ case "ReadonlyArray":
63
+ case "Iterable":
64
+ case "IterableIterator":
65
+ case "Iterator":
66
+ case "AsyncIterable":
67
+ case "AsyncIterableIterator":
68
+ case "Generator":
69
+ case "AsyncGenerator":
70
+ case "Set":
71
+ case "ReadonlySet":
72
+ case "JSArray":
73
+ case "IEnumerable":
74
+ case "IReadOnlyList":
75
+ case "List":
76
+ return type.typeArguments[0] ? [type.typeArguments[0]] : undefined;
77
+ case "Map":
78
+ case "ReadonlyMap":
79
+ return type.typeArguments[0] && type.typeArguments[1]
80
+ ? [
81
+ {
82
+ kind: "tupleType",
83
+ elementTypes: [type.typeArguments[0], type.typeArguments[1]],
84
+ },
85
+ ]
86
+ : undefined;
87
+ default:
88
+ return undefined;
89
+ }
90
+ }
91
+ return undefined;
92
+ };
22
93
  // Filter out holes and spreads for type analysis
23
94
  const regularElements = elements.filter((e) => e !== undefined && e.kind !== "spread");
24
- if (regularElements.length === 0) {
95
+ const spreadElements = elements.filter((e) => e !== undefined && e.kind === "spread");
96
+ if (regularElements.length === 0 && spreadElements.length === 0) {
25
97
  // Empty array - use fallback
26
98
  return fallbackType;
27
99
  }
@@ -59,8 +131,11 @@ const computeArrayElementType = (elements, fallbackType) => {
59
131
  allBooleanLiterals = false;
60
132
  }
61
133
  }
134
+ const hasOnlyRegularElements = regularElements.length > 0 && spreadElements.length === 0;
62
135
  // All numeric literals - determine widest type
63
- if (allNumericLiterals && numericIntents.length > 0) {
136
+ if (hasOnlyRegularElements &&
137
+ allNumericLiterals &&
138
+ numericIntents.length > 0) {
64
139
  // Any Double → number (emits as "double" in C#)
65
140
  if (numericIntents.includes("Double") ||
66
141
  numericIntents.includes("Single")) {
@@ -74,11 +149,11 @@ const computeArrayElementType = (elements, fallbackType) => {
74
149
  return { kind: "primitiveType", name: "int" };
75
150
  }
76
151
  // All string literals
77
- if (allStringLiterals) {
152
+ if (hasOnlyRegularElements && allStringLiterals) {
78
153
  return { kind: "primitiveType", name: "string" };
79
154
  }
80
155
  // All boolean literals
81
- if (allBooleanLiterals) {
156
+ if (hasOnlyRegularElements && allBooleanLiterals) {
82
157
  return { kind: "primitiveType", name: "boolean" };
83
158
  }
84
159
  // Mixed or complex - fall back to TS inference
@@ -93,9 +168,16 @@ const computeArrayElementType = (elements, fallbackType) => {
93
168
  }
94
169
  knownTypes.push(t);
95
170
  }
96
- const first = knownTypes[0];
97
- if (first && knownTypes.every((t) => typesEqual(first, t))) {
98
- return first;
171
+ for (const spread of spreadElements) {
172
+ const spreadTypes = extractSpreadElementTypes(spread.expression.inferredType);
173
+ if (!spreadTypes) {
174
+ return fallbackType;
175
+ }
176
+ knownTypes.push(...spreadTypes);
177
+ }
178
+ const merged = mergeElementTypes(knownTypes);
179
+ if (merged) {
180
+ return merged;
99
181
  }
100
182
  return fallbackType;
101
183
  };
@@ -113,8 +195,11 @@ const computeArrayElementType = (elements, fallbackType) => {
113
195
  * Pass `undefined` explicitly when no contextual type exists.
114
196
  */
115
197
  export const convertArrayLiteral = (node, ctx, expectedType) => {
198
+ const contextualArrayType = expectedType?.kind === "arrayType" && !containsTypeParameter(expectedType)
199
+ ? expectedType
200
+ : undefined;
116
201
  // Determine element expected type from array expected type
117
- const expectedElementType = expectedType?.kind === "arrayType" ? expectedType.elementType : undefined;
202
+ const expectedElementType = contextualArrayType?.elementType;
118
203
  // Convert all elements, passing expected element type for contextual typing
119
204
  const elements = node.elements.map((elem) => {
120
205
  if (ts.isOmittedExpression(elem)) {
@@ -136,8 +221,8 @@ export const convertArrayLiteral = (node, ctx, expectedType) => {
136
221
  // 1. Expected type from context (e.g., LHS annotation, parameter type)
137
222
  // 2. Literal-form inference (derive from element types)
138
223
  // 3. Default: number[] (double[]) for ergonomics
139
- const inferredType = expectedType?.kind === "arrayType"
140
- ? expectedType
224
+ const inferredType = contextualArrayType
225
+ ? contextualArrayType
141
226
  : (() => {
142
227
  // No expected type - derive from element types
143
228
  const elementType = computeArrayElementType(elements, undefined);
@@ -191,6 +276,249 @@ const getPropertyExpectedType = (propName, expectedType, ctx) => {
191
276
  }
192
277
  return undefined;
193
278
  };
279
+ const unwrapDeterministicKeyExpression = (expr) => {
280
+ let current = expr;
281
+ for (;;) {
282
+ if (ts.isParenthesizedExpression(current)) {
283
+ current = current.expression;
284
+ continue;
285
+ }
286
+ if (ts.isAsExpression(current) ||
287
+ ts.isTypeAssertionExpression(current) ||
288
+ ts.isSatisfiesExpression(current)) {
289
+ current = current.expression;
290
+ continue;
291
+ }
292
+ return current;
293
+ }
294
+ };
295
+ const tryResolveDeterministicObjectKeyNameFromSyntax = (expr, ctx, seenSymbols = new Set()) => {
296
+ const current = unwrapDeterministicKeyExpression(expr);
297
+ if (ts.isStringLiteral(current) ||
298
+ ts.isNoSubstitutionTemplateLiteral(current) ||
299
+ ts.isNumericLiteral(current)) {
300
+ return String(current.text);
301
+ }
302
+ if (!ts.isIdentifier(current)) {
303
+ return undefined;
304
+ }
305
+ const symbol = ctx.checker.getSymbolAtLocation(current);
306
+ if (!symbol || seenSymbols.has(symbol)) {
307
+ return undefined;
308
+ }
309
+ seenSymbols.add(symbol);
310
+ const visitDeclarations = (target) => {
311
+ for (const decl of target.getDeclarations() ?? []) {
312
+ if (ts.isVariableDeclaration(decl) &&
313
+ decl.initializer &&
314
+ ts.isVariableDeclarationList(decl.parent) &&
315
+ (decl.parent.flags & ts.NodeFlags.Const) !== 0) {
316
+ const resolved = tryResolveDeterministicObjectKeyNameFromSyntax(decl.initializer, ctx, seenSymbols);
317
+ if (resolved !== undefined)
318
+ return resolved;
319
+ }
320
+ }
321
+ return undefined;
322
+ };
323
+ const direct = visitDeclarations(symbol);
324
+ if (direct !== undefined)
325
+ return direct;
326
+ if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {
327
+ const aliased = ctx.checker.getAliasedSymbol(symbol);
328
+ if (!seenSymbols.has(aliased)) {
329
+ seenSymbols.add(aliased);
330
+ return visitDeclarations(aliased);
331
+ }
332
+ }
333
+ return undefined;
334
+ };
335
+ const resolveObjectLiteralMemberKey = (name, ctx) => {
336
+ if (ts.isIdentifier(name) ||
337
+ ts.isStringLiteral(name) ||
338
+ ts.isNoSubstitutionTemplateLiteral(name) ||
339
+ ts.isNumericLiteral(name)) {
340
+ const keyName = String(name.text);
341
+ return { key: keyName, keyName };
342
+ }
343
+ if (!ts.isComputedPropertyName(name)) {
344
+ return { key: "", keyName: undefined };
345
+ }
346
+ const keyName = tryResolveDeterministicObjectKeyNameFromSyntax(name.expression, ctx);
347
+ const computedKey = convertExpression(unwrapDeterministicKeyExpression(name.expression), ctx, undefined);
348
+ return { key: keyName ?? computedKey, keyName };
349
+ };
350
+ const methodUsesObjectLiteralThis = (method) => {
351
+ let found = false;
352
+ const visit = (current) => {
353
+ if (found)
354
+ return;
355
+ if (current.kind === ts.SyntaxKind.ThisKeyword) {
356
+ found = true;
357
+ return;
358
+ }
359
+ if (ts.isFunctionExpression(current) ||
360
+ ts.isFunctionDeclaration(current) ||
361
+ ts.isMethodDeclaration(current) ||
362
+ ts.isGetAccessorDeclaration(current) ||
363
+ ts.isSetAccessorDeclaration(current)) {
364
+ if (current !== method) {
365
+ return;
366
+ }
367
+ }
368
+ ts.forEachChild(current, visit);
369
+ };
370
+ if (method.body) {
371
+ visit(method.body);
372
+ }
373
+ return found;
374
+ };
375
+ const isNullishPrimitive = (type) => type.kind === "primitiveType" &&
376
+ (type.name === "null" || type.name === "undefined");
377
+ const normalizeExpectedFunctionType = (expectedType, ctx) => {
378
+ if (!expectedType)
379
+ return undefined;
380
+ if (expectedType.kind === "functionType")
381
+ return expectedType;
382
+ const delegated = ctx.typeSystem.delegateToFunctionType(expectedType);
383
+ if (delegated)
384
+ return delegated;
385
+ if (expectedType.kind !== "unionType")
386
+ return undefined;
387
+ const candidates = expectedType.types
388
+ .filter((member) => !!member && !isNullishPrimitive(member))
389
+ .map((member) => member.kind === "functionType"
390
+ ? member
391
+ : ctx.typeSystem.delegateToFunctionType(member))
392
+ .filter((member) => member !== undefined);
393
+ return candidates.length === 1 ? candidates[0] : undefined;
394
+ };
395
+ const getExpectedFunctionParameterTypes = (expectedType, ctx) => {
396
+ const fnType = normalizeExpectedFunctionType(expectedType, ctx);
397
+ return fnType?.parameters.map((param) => param.type);
398
+ };
399
+ const convertObjectLiteralMethodParameters = (parameters, ctx, expectedType) => {
400
+ const expectedParamTypes = getExpectedFunctionParameterTypes(expectedType, ctx);
401
+ return parameters.map((param, index) => {
402
+ const explicitType = param.type
403
+ ? ctx.typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(param.type))
404
+ : undefined;
405
+ const paramType = explicitType ?? expectedParamTypes?.[index];
406
+ return {
407
+ kind: "parameter",
408
+ pattern: convertBindingName(param.name, ctx),
409
+ type: paramType,
410
+ initializer: param.initializer
411
+ ? convertExpression(param.initializer, ctx, paramType)
412
+ : undefined,
413
+ isOptional: !!param.questionToken,
414
+ isRest: !!param.dotDotDotToken,
415
+ passing: "value",
416
+ };
417
+ });
418
+ };
419
+ const buildObjectLiteralMethodFunctionType = (method, ctx, expectedType) => {
420
+ const expectedFnType = normalizeExpectedFunctionType(expectedType, ctx);
421
+ const parameters = convertObjectLiteralMethodParameters(method.parameters, ctx, expectedFnType ?? expectedType);
422
+ const declaredReturnType = method.type
423
+ ? ctx.typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(method.type))
424
+ : undefined;
425
+ return {
426
+ kind: "functionType",
427
+ parameters,
428
+ returnType: declaredReturnType ??
429
+ expectedFnType?.returnType ?? { kind: "unknownType" },
430
+ };
431
+ };
432
+ const getSynthesizedPropertyType = (expr, widenNumericLiterals) => {
433
+ if (widenNumericLiterals &&
434
+ expr.kind === "literal" &&
435
+ typeof expr.value === "number") {
436
+ return { kind: "primitiveType", name: "number" };
437
+ }
438
+ return expr.inferredType;
439
+ };
440
+ const getProvisionalAccessorPropertyType = (getter, setter, expectedType, ctx) => {
441
+ const getterType = getter?.type
442
+ ? ctx.typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(getter.type))
443
+ : undefined;
444
+ const setterValueParam = setter?.parameters[0];
445
+ const setterType = setterValueParam?.type !== undefined
446
+ ? ctx.typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(setterValueParam.type))
447
+ : undefined;
448
+ return getterType ?? setterType ?? expectedType;
449
+ };
450
+ const collectSynthesizedObjectMembers = (properties, pendingMethods, pendingAccessors, widenNumericLiterals) => {
451
+ const synthesizedMembers = [];
452
+ for (const prop of properties) {
453
+ if (prop.kind === "property") {
454
+ const keyName = typeof prop.key === "string"
455
+ ? prop.key
456
+ : prop.key.kind === "literal" && typeof prop.key.value === "string"
457
+ ? prop.key.value
458
+ : undefined;
459
+ if (!keyName) {
460
+ return {
461
+ ok: false,
462
+ failureReason: "Only identifier and computed string-literal keys are supported",
463
+ };
464
+ }
465
+ const propType = getSynthesizedPropertyType(prop.value, widenNumericLiterals);
466
+ if (!propType ||
467
+ propType.kind === "unknownType" ||
468
+ propType.kind === "anyType") {
469
+ return {
470
+ ok: false,
471
+ failureReason: `Property '${keyName}' type cannot be recovered deterministically`,
472
+ };
473
+ }
474
+ synthesizedMembers.push({
475
+ kind: "propertySignature",
476
+ name: keyName,
477
+ type: propType,
478
+ isOptional: false,
479
+ isReadonly: false,
480
+ });
481
+ continue;
482
+ }
483
+ const spreadType = prop.expression.inferredType;
484
+ if (spreadType?.kind !== "objectType") {
485
+ return {
486
+ ok: false,
487
+ failureReason: "Spread sources must have a deterministically known object literal shape",
488
+ };
489
+ }
490
+ for (const member of spreadType.members) {
491
+ if (member.kind === "propertySignature") {
492
+ synthesizedMembers.push(member);
493
+ }
494
+ }
495
+ }
496
+ for (const method of pendingMethods) {
497
+ synthesizedMembers.push({
498
+ kind: "propertySignature",
499
+ name: method.keyName,
500
+ type: method.functionType,
501
+ isOptional: false,
502
+ isReadonly: false,
503
+ });
504
+ }
505
+ for (const accessor of pendingAccessors) {
506
+ if (!accessor.propertyType) {
507
+ return {
508
+ ok: false,
509
+ failureReason: `Accessor '${accessor.memberName}' type cannot be recovered deterministically`,
510
+ };
511
+ }
512
+ synthesizedMembers.push({
513
+ kind: "propertySignature",
514
+ name: accessor.memberName,
515
+ type: accessor.propertyType,
516
+ isOptional: false,
517
+ isReadonly: false,
518
+ });
519
+ }
520
+ return { ok: true, members: synthesizedMembers };
521
+ };
194
522
  /**
195
523
  * Convert object literal expression
196
524
  *
@@ -201,6 +529,9 @@ const getPropertyExpectedType = (propName, expectedType, ctx) => {
201
529
  */
202
530
  export const convertObjectLiteral = (node, ctx, expectedType) => {
203
531
  const properties = [];
532
+ const behaviorMembers = [];
533
+ const pendingMethods = [];
534
+ const accessorGroups = new Map();
204
535
  // Contextual type priority:
205
536
  // 1) expectedType threaded from the parent converter (return, assignment, parameter, etc.)
206
537
  // 2) AST-based contextual typing from explicit TypeNodes (getContextualType)
@@ -242,27 +573,18 @@ export const convertObjectLiteral = (node, ctx, expectedType) => {
242
573
  ts.isShorthandPropertyAssignment(p));
243
574
  const shouldLowerToDictionary = isObjectLikeContext && isPlainObjectLiteralAst;
244
575
  const dictionaryValueExpectedType = { kind: "unknownType" };
576
+ const getObjectLiteralPropertyExpectedType = (keyName) => keyName
577
+ ? (getPropertyExpectedType(keyName, expectedType, ctx) ??
578
+ (shouldLowerToDictionary ? dictionaryValueExpectedType : undefined))
579
+ : shouldLowerToDictionary
580
+ ? dictionaryValueExpectedType
581
+ : undefined;
245
582
  // Track if we have any spreads (needed for emitter IIFE lowering)
246
583
  let hasSpreads = false;
247
584
  node.properties.forEach((prop) => {
248
585
  if (ts.isPropertyAssignment(prop)) {
249
- const keyName = ts.isIdentifier(prop.name)
250
- ? prop.name.text
251
- : ts.isStringLiteral(prop.name)
252
- ? prop.name.text
253
- : undefined;
254
- const key = ts.isComputedPropertyName(prop.name)
255
- ? convertExpression(prop.name.expression, ctx, undefined)
256
- : ts.isIdentifier(prop.name)
257
- ? prop.name.text
258
- : String(prop.name.text);
259
- // Look up property expected type from parent expected type
260
- const propExpectedType = keyName
261
- ? (getPropertyExpectedType(keyName, expectedType, ctx) ??
262
- (shouldLowerToDictionary ? dictionaryValueExpectedType : undefined))
263
- : shouldLowerToDictionary
264
- ? dictionaryValueExpectedType
265
- : undefined;
586
+ const { key, keyName } = resolveObjectLiteralMemberKey(prop.name, ctx);
587
+ const propExpectedType = getObjectLiteralPropertyExpectedType(keyName);
266
588
  properties.push({
267
589
  kind: "property",
268
590
  key,
@@ -305,32 +627,44 @@ export const convertObjectLiteral = (node, ctx, expectedType) => {
305
627
  });
306
628
  }
307
629
  else if (ts.isMethodDeclaration(prop)) {
308
- const keyName = ts.isIdentifier(prop.name)
309
- ? prop.name.text
310
- : ts.isStringLiteral(prop.name)
311
- ? prop.name.text
312
- : undefined;
313
- const key = ts.isComputedPropertyName(prop.name)
314
- ? convertExpression(prop.name.expression, ctx, undefined)
315
- : ts.isIdentifier(prop.name)
316
- ? prop.name.text
317
- : String(prop.name.text);
318
- const propExpectedType = keyName
319
- ? (getPropertyExpectedType(keyName, expectedType, ctx) ??
320
- (shouldLowerToDictionary ? dictionaryValueExpectedType : undefined))
321
- : shouldLowerToDictionary
322
- ? dictionaryValueExpectedType
323
- : undefined;
324
- const methodModifiers = prop.modifiers?.filter(ts.isModifier);
325
- const methodAsFunctionExpr = ts.setTextRange(ts.factory.createFunctionExpression(methodModifiers, prop.asteriskToken, undefined, prop.typeParameters, prop.parameters, prop.type, prop.body ?? ts.factory.createBlock([], true)), prop);
326
- properties.push({
327
- kind: "property",
630
+ const { key, keyName } = resolveObjectLiteralMemberKey(prop.name, ctx);
631
+ if (!keyName) {
632
+ ctx.diagnostics.push(createDiagnostic("TSN7403", "error", "Object literal cannot be synthesized: computed method key is not a deterministically known string/number literal", getSourceSpan(prop), "Use an identifier, string literal key, or explicit type annotation."));
633
+ return;
634
+ }
635
+ const propExpectedType = getObjectLiteralPropertyExpectedType(keyName);
636
+ pendingMethods.push({
328
637
  key,
329
- value: convertExpression(methodAsFunctionExpr, ctx, propExpectedType),
330
- shorthand: false,
638
+ keyName,
639
+ node: prop,
640
+ propExpectedType,
641
+ capturesObjectLiteralThis: methodUsesObjectLiteralThis(prop),
642
+ functionType: buildObjectLiteralMethodFunctionType(prop, ctx, propExpectedType),
331
643
  });
332
644
  }
645
+ else if (ts.isGetAccessorDeclaration(prop) ||
646
+ ts.isSetAccessorDeclaration(prop)) {
647
+ const { keyName: memberName } = resolveObjectLiteralMemberKey(prop.name, ctx);
648
+ if (!memberName) {
649
+ ctx.diagnostics.push(createDiagnostic("TSN7403", "error", "Object literal cannot be synthesized: computed accessor key is not a deterministically known string/number literal", getSourceSpan(prop), "Use an identifier, string literal key, or explicit type annotation."));
650
+ return;
651
+ }
652
+ const existing = accessorGroups.get(memberName) ?? {};
653
+ if (ts.isGetAccessorDeclaration(prop)) {
654
+ existing.getter = prop;
655
+ }
656
+ else {
657
+ existing.setter = prop;
658
+ }
659
+ accessorGroups.set(memberName, existing);
660
+ }
333
661
  });
662
+ const pendingAccessors = Array.from(accessorGroups.entries()).map(([memberName, group]) => ({
663
+ memberName,
664
+ getter: group.getter,
665
+ setter: group.setter,
666
+ propertyType: getProvisionalAccessorPropertyType(group.getter, group.setter, getObjectLiteralPropertyExpectedType(memberName), ctx),
667
+ }));
334
668
  let contextualType = contextualCandidate;
335
669
  if (isObjectLikeContext) {
336
670
  contextualType = shouldLowerToDictionary
@@ -349,90 +683,60 @@ export const convertObjectLiteral = (node, ctx, expectedType) => {
349
683
  ctx.diagnostics.push(createDiagnostic("TSN7403", "error", `Object literal cannot be synthesized: ${eligibility.reason}`, getSourceSpan(node), "Use an explicit type annotation, or restructure to use only identifier keys, string literal keys, spread identifiers with type annotations, and function-valued properties."));
350
684
  }
351
685
  else {
352
- // Extract property info from already-converted properties (AST-based)
353
- const propInfos = [];
354
- let canSynthesize = true;
355
- let synthesisFailureReason;
356
- for (const prop of properties) {
357
- if (prop.kind === "property") {
358
- // Get property name (identifier or computed string-literal keys).
359
- const keyName = typeof prop.key === "string"
360
- ? prop.key
361
- : prop.key.kind === "literal" &&
362
- typeof prop.key.value === "string"
363
- ? prop.key.value
364
- : undefined;
365
- if (!keyName) {
366
- canSynthesize = false;
367
- synthesisFailureReason =
368
- "Only identifier and computed string-literal keys are supported";
369
- break;
370
- }
371
- // Get type from converted expression's inferredType
372
- const propType = prop.value.inferredType;
373
- if (!propType ||
374
- propType.kind === "unknownType" ||
375
- propType.kind === "anyType") {
376
- // Cannot synthesize if property type is unknown/any
377
- canSynthesize = false;
378
- synthesisFailureReason = `Property '${keyName}' type cannot be recovered deterministically`;
379
- break;
380
- }
381
- propInfos.push({
382
- name: keyName,
383
- type: propType,
384
- optional: false,
385
- readonly: false,
386
- });
387
- }
388
- else if (prop.kind === "spread") {
389
- // For spreads, merge properties from the spread source's objectType
390
- const spreadType = prop.expression.inferredType;
391
- if (spreadType?.kind === "objectType") {
392
- for (const member of spreadType.members) {
393
- if (member.kind === "propertySignature") {
394
- propInfos.push({
395
- name: member.name,
396
- type: member.type,
397
- optional: member.isOptional,
398
- readonly: member.isReadonly,
399
- });
400
- }
401
- }
402
- }
403
- else {
404
- canSynthesize = false;
405
- synthesisFailureReason =
406
- "Spread sources must have a deterministically known object literal shape";
407
- break;
408
- }
409
- }
410
- }
411
- if (canSynthesize) {
412
- // DETERMINISTIC: synthesize an objectType shape from the AST, then allow the
413
- // anonymous-type-lowering pass to generate a nominal type (class) for emission.
686
+ const synthesized = collectSynthesizedObjectMembers(properties, pendingMethods, pendingAccessors, pendingAccessors.length > 0);
687
+ if (synthesized.ok && synthesized.members) {
414
688
  contextualType = {
415
689
  kind: "objectType",
416
- members: propInfos.map((p) => ({
417
- kind: "propertySignature",
418
- name: p.name,
419
- type: p.type,
420
- isOptional: p.optional,
421
- isReadonly: p.readonly,
422
- })),
690
+ members: synthesized.members,
423
691
  };
424
692
  }
425
693
  else {
426
- ctx.diagnostics.push(createDiagnostic("TSN7403", "error", `Object literal cannot be synthesized: ${synthesisFailureReason ?? "not supported in this context"}`, getSourceSpan(node), "Use an explicit type annotation, or restructure to use only identifier keys, string literal keys, spread identifiers with type annotations, and function-valued properties."));
694
+ ctx.diagnostics.push(createDiagnostic("TSN7403", "error", `Object literal cannot be synthesized: ${synthesized.failureReason ?? "not supported in this context"}`, getSourceSpan(node), "Use an explicit type annotation, or restructure to use only identifier keys, string literal keys, spread identifiers with type annotations, and function-valued properties."));
427
695
  }
428
696
  }
429
697
  }
698
+ const objectLiteralThisType = contextualType && contextualType.kind !== "dictionaryType"
699
+ ? contextualType
700
+ : undefined;
701
+ const objectBehaviorContext = objectLiteralThisType
702
+ ? { ...ctx, objectLiteralThisType }
703
+ : ctx;
704
+ for (const pendingMethod of pendingMethods) {
705
+ const methodPrelude = createObjectLiteralMethodArgumentPrelude(pendingMethod.node);
706
+ const methodBody = pendingMethod.node.body
707
+ ? methodPrelude.length > 0
708
+ ? ts.factory.updateBlock(pendingMethod.node.body, [
709
+ ...methodPrelude,
710
+ ...pendingMethod.node.body.statements,
711
+ ])
712
+ : pendingMethod.node.body
713
+ : ts.factory.createBlock(methodPrelude, true);
714
+ const methodModifiers = pendingMethod.node.modifiers?.filter(ts.isModifier);
715
+ const methodAsFunctionExpr = ts.setTextRange(ts.factory.createFunctionExpression(methodModifiers, pendingMethod.node.asteriskToken, undefined, pendingMethod.node.typeParameters, pendingMethod.node.parameters, pendingMethod.node.type, methodBody), pendingMethod.node);
716
+ const convertedValue = convertExpression(methodAsFunctionExpr, objectBehaviorContext, pendingMethod.propExpectedType);
717
+ properties.push({
718
+ kind: "property",
719
+ key: pendingMethod.key,
720
+ value: convertedValue.kind === "functionExpression" &&
721
+ pendingMethod.capturesObjectLiteralThis
722
+ ? {
723
+ ...convertedValue,
724
+ capturesObjectLiteralThis: true,
725
+ }
726
+ : convertedValue,
727
+ shorthand: false,
728
+ });
729
+ }
730
+ for (const pendingAccessor of pendingAccessors) {
731
+ behaviorMembers.push(convertAccessorProperty(pendingAccessor.memberName, pendingAccessor.getter, pendingAccessor.setter, objectBehaviorContext, undefined));
732
+ }
430
733
  // DETERMINISTIC TYPING: Object's inferredType comes from contextualType
431
734
  // (which may be from LHS annotation or synthesized type).
432
735
  // We don't derive from properties because that would require TS inference.
433
736
  return {
434
737
  kind: "object",
435
738
  properties,
739
+ behaviorMembers: behaviorMembers.length > 0 ? behaviorMembers : undefined,
436
740
  inferredType: contextualType, // Use contextual type if available
437
741
  sourceSpan: getSourceSpan(node),
438
742
  contextualType,