tsc-ts 0.0.6 → 0.0.7

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/api/compiler/ast.d.ts +57 -18
  2. package/dist/api/compiler/ast.js +134 -39
  3. package/dist/api/compiler/binder-utilities.js +2 -2
  4. package/dist/api/compiler/binder.js +6 -6
  5. package/dist/api/compiler/checker-calls.js +4 -4
  6. package/dist/api/compiler/checker-contextual.js +1 -1
  7. package/dist/api/compiler/checker-emit-helpers.d.ts +2 -1
  8. package/dist/api/compiler/checker-emit-helpers.js +5 -4
  9. package/dist/api/compiler/checker-grammar.js +5 -5
  10. package/dist/api/compiler/checker-inference.d.ts +20 -10
  11. package/dist/api/compiler/checker-inference.js +48 -25
  12. package/dist/api/compiler/checker-members.d.ts +27 -0
  13. package/dist/api/compiler/checker-members.js +165 -0
  14. package/dist/api/compiler/checker-modules.d.ts +14 -0
  15. package/dist/api/compiler/checker-modules.js +73 -1
  16. package/dist/api/compiler/checker.d.ts +16 -0
  17. package/dist/api/compiler/checker.js +17 -0
  18. package/dist/api/compiler/diagnostics.js +9 -5
  19. package/dist/api/compiler/parallel-parse.js +3 -1
  20. package/dist/api/compiler/parser.js +33 -23
  21. package/dist/api/compiler/printer/factory-base.js +8 -8
  22. package/dist/api/compiler/printer/factory.js +4 -4
  23. package/dist/api/compiler/printer/printer.js +6 -6
  24. package/dist/api/compiler/program-checker.d.ts +5 -0
  25. package/dist/api/compiler/program-checker.js +11 -1
  26. package/dist/api/compiler/relations.js +8 -3
  27. package/dist/api/compiler/reparser.js +13 -5
  28. package/dist/api/compiler/transformers/class-fields.js +3 -3
  29. package/dist/api/compiler/transformers/commonjs-module.js +3 -3
  30. package/dist/api/compiler/transformers/declarations-pseudo.js +2 -2
  31. package/dist/api/compiler/transformers/module.js +2 -2
  32. package/dist/api/compiler/transformers/optional-chain.js +3 -3
  33. package/dist/api/compiler/transformers/subtree-facts.js +2 -2
  34. package/dist/api/compiler/transformers/tagged-template.js +2 -2
  35. package/dist/api/compiler/transformers/type-eraser.js +2 -2
  36. package/dist/api/compiler/transformers/visitor-base.js +3 -3
  37. package/dist/api/compiler/tsconfig.js +2 -2
  38. package/package.json +8 -8
  39. package/src/compiler/ast.ts +184 -59
  40. package/src/compiler/binder-utilities.ts +2 -1
  41. package/src/compiler/binder.ts +11 -5
  42. package/src/compiler/checker-calls.ts +5 -3
  43. package/src/compiler/checker-contextual.ts +1 -1
  44. package/src/compiler/checker-emit-helpers.ts +5 -4
  45. package/src/compiler/checker-grammar.ts +5 -3
  46. package/src/compiler/checker-inference.ts +49 -25
  47. package/src/compiler/checker-members.ts +164 -0
  48. package/src/compiler/checker-modules.ts +92 -1
  49. package/src/compiler/checker.ts +19 -0
  50. package/src/compiler/decoder.ts +5 -4
  51. package/src/compiler/diagnostics.ts +10 -5
  52. package/src/compiler/encoder.ts +10 -5
  53. package/src/compiler/parallel-parse.ts +3 -1
  54. package/src/compiler/parser.ts +53 -23
  55. package/src/compiler/printer/factory-base.ts +14 -7
  56. package/src/compiler/printer/factory.ts +5 -3
  57. package/src/compiler/printer/printer.ts +8 -6
  58. package/src/compiler/program-checker.ts +10 -1
  59. package/src/compiler/relations.ts +10 -2
  60. package/src/compiler/reparser.ts +14 -4
  61. package/src/compiler/transformers/class-fields.ts +12 -2
  62. package/src/compiler/transformers/commonjs-module.ts +3 -2
  63. package/src/compiler/transformers/declarations-pseudo.ts +2 -1
  64. package/src/compiler/transformers/declarations.ts +1 -1
  65. package/src/compiler/transformers/module.ts +2 -1
  66. package/src/compiler/transformers/optional-chain.ts +5 -2
  67. package/src/compiler/transformers/subtree-facts.ts +2 -1
  68. package/src/compiler/transformers/tagged-template.ts +2 -2
  69. package/src/compiler/transformers/type-eraser.ts +2 -1
  70. package/src/compiler/transformers/visitor-base.ts +10 -5
  71. package/src/compiler/tsconfig.ts +2 -1
@@ -57,7 +57,8 @@ class PendingHelperCheck {
57
57
  export class EmitHelperFileLinks {
58
58
  helpersModule: Symbol | undefined = undefined;
59
59
  requestedHelpers: number = ExternalEmitHelpers.None;
60
- pending: PendingHelperCheck[] = [];
60
+ /** Created on the first check; most files have none. */
61
+ pending: PendingHelperCheck[] | undefined = undefined;
61
62
  }
62
63
 
63
64
  function fileLinks(c: Checker, file: SourceFile): EmitHelperFileLinks {
@@ -86,7 +87,7 @@ export function checkExternalEmitHelpers(c: Checker, location: Node, helpers: Ex
86
87
  const fileIndex = c.fileIndex.get(file);
87
88
  const checkingIndex = c.fileIndex.get(checking);
88
89
  if (fileIndex !== undefined && checkingIndex !== undefined && fileIndex < checkingIndex) {
89
- fileLinks(c, file).pending.push(new PendingHelperCheck(location, helpers));
90
+ (fileLinks(c, file).pending ??= []).push(new PendingHelperCheck(location, helpers));
90
91
  return;
91
92
  }
92
93
  }
@@ -141,9 +142,9 @@ export function checkExternalEmitHelpers(c: Checker, location: Node, helpers: Ex
141
142
  /** Runs the helper checks for nodes of `file` that were reached while checking later files. */
142
143
  export function checkPendingExternalEmitHelpers(c: Checker, file: SourceFile): void {
143
144
  const links = c.emitHelperFileLinks.get(file);
144
- if (links === undefined || links.pending.length === 0) return;
145
+ if (links?.pending === undefined) return;
145
146
  const pending = links.pending;
146
- links.pending = [];
147
+ links.pending = undefined;
147
148
  for (const check of pending) checkExternalEmitHelpers(c, check.location, check.helpers);
148
149
  }
149
150
 
@@ -80,6 +80,8 @@ import {
80
80
  getTypeArguments,
81
81
  getTypeParameters,
82
82
  isDeclarationNode,
83
+ callQuestionDotToken,
84
+ callTypeArguments,
83
85
  } from './ast.js';
84
86
  import {
85
87
  canHaveDecorators,
@@ -1297,7 +1299,7 @@ function checkGrammarDecorator(g: GrammarChecker, decorator: Decorator): boolean
1297
1299
  const call = node as CallExpression;
1298
1300
  if (!canHaveCallExpression) errorNode = node;
1299
1301
  // A `?.` token comes first, so it wins over an earlier error node.
1300
- if (call.questionDotToken !== undefined) errorNode = call.questionDotToken;
1302
+ if (callQuestionDotToken(call) !== undefined) errorNode = callQuestionDotToken(call);
1301
1303
  node = call.expression;
1302
1304
  canHaveCallExpression = false;
1303
1305
  continue;
@@ -3305,7 +3307,7 @@ function checkGrammarCallExpression(g: GrammarChecker, node: CallExpression): vo
3305
3307
  (callee as MetaProperty).keywordToken === SyntaxKind.ImportKeyword &&
3306
3308
  (callee as MetaProperty).name.text === 'defer');
3307
3309
  if (isImportCall) checkGrammarImportCallExpression(g, node);
3308
- else checkGrammarTypeArguments(g, node.typeArguments);
3310
+ else checkGrammarTypeArguments(g, callTypeArguments(node));
3309
3311
  }
3310
3312
 
3311
3313
  function checkGrammarImportCallExpression(g: GrammarChecker, node: CallExpression): boolean {
@@ -3331,7 +3333,7 @@ function checkGrammarImportCallExpression(g: GrammarChecker, node: CallExpressio
3331
3333
  node,
3332
3334
  Diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_es2022_esnext_commonjs_amd_system_umd_node16_node18_node20_or_nodenext,
3333
3335
  );
3334
- if (node.typeArguments !== undefined)
3336
+ if (callTypeArguments(node) !== undefined)
3335
3337
  return grammarErrorOnNode(
3336
3338
  g,
3337
3339
  node,
@@ -293,20 +293,27 @@ export class IntraExpressionInferenceSite {
293
293
  }
294
294
  }
295
295
 
296
- export class InferenceContext {
296
+ /**
297
+ * The inferences for a set of type parameters. The context is its own fixing mapper (`mapper`), and
298
+ * each read of `nonFixingMapper` returns a new mapper that reads the context: no mapper the context
299
+ * holds refers back to it, so a context whose inference is done is freed by reference counting
300
+ * rather than by the cycle collector.
301
+ */
302
+ export class InferenceContext extends TypeMapper {
303
+ readonly c: Checker;
297
304
  inferences: InferenceInfo[];
298
305
  /** The generic signature inferences are made for, if any. */
299
306
  readonly signature: Signature | undefined;
300
307
  flags: InferenceFlags;
301
308
  readonly compareTypes: TypeComparer;
302
- /** Fixes inferences. */
303
- mapper: TypeMapper;
304
- nonFixingMapper: TypeMapper;
309
+ /** Combined after the non-fixing mapper (conditional types' `infer` constraints), if any. */
310
+ nonFixingOuter: TypeMapper | undefined = undefined;
305
311
  /** Maps to inferences from the return type, if any. */
306
312
  returnMapper: TypeMapper | undefined = undefined;
307
313
  outerReturnMapper: TypeMapper | undefined = undefined;
308
314
  inferredTypeParameters: Type[] | undefined = undefined;
309
- intraExpressionInferenceSites: IntraExpressionInferenceSite[] = [];
315
+ /** Created on the first site; most contexts get none. */
316
+ intraExpressionInferenceSites: IntraExpressionInferenceSite[] | undefined = undefined;
310
317
 
311
318
  constructor(
312
319
  c: Checker,
@@ -315,45 +322,61 @@ export class InferenceContext {
315
322
  flags: InferenceFlags,
316
323
  compareTypes: TypeComparer,
317
324
  ) {
325
+ super();
326
+ this.c = c;
318
327
  this.inferences = inferences;
319
328
  this.signature = signature;
320
329
  this.flags = flags;
321
330
  this.compareTypes = compareTypes;
322
- this.mapper = new InferenceTypeMapper(c, this, true);
323
- this.nonFixingMapper = new InferenceTypeMapper(c, this, false);
324
331
  }
325
- }
326
332
 
327
- /** Maps a context's type parameters to their inferred types, optionally fixing them. */
328
- export class InferenceTypeMapper extends TypeMapper {
329
- readonly c: Checker;
330
- readonly context: InferenceContext;
331
- readonly fixing: boolean;
333
+ /** Fixes inferences. */
334
+ get mapper(): TypeMapper {
335
+ return this;
336
+ }
332
337
 
333
- constructor(c: Checker, context: InferenceContext, fixing: boolean) {
334
- super();
335
- this.c = c;
336
- this.context = context;
337
- this.fixing = fixing;
338
+ /** Maps to the current inferences without fixing them. */
339
+ get nonFixingMapper(): TypeMapper {
340
+ const mapper = new NonFixingInferenceMapper(this);
341
+ const outer = this.nonFixingOuter;
342
+ return outer !== undefined ? combineTypeMappers(this.c, mapper, outer) : mapper;
338
343
  }
339
344
 
340
345
  map(type: Type): Type {
341
- const inferences = this.context.inferences;
346
+ const inferences = this.inferences;
342
347
  for (let i = 0; i < inferences.length; i++) {
343
348
  const inference = inferences[i]!;
344
349
  if (type !== inference.typeParameter) continue;
345
- if (this.fixing && !inference.isFixed) {
350
+ if (!inference.isFixed) {
346
351
  // Infer from intra-expression sites before committing to an inference.
347
- inferFromIntraExpressionSites(this.c, this.context);
352
+ inferFromIntraExpressionSites(this.c, this);
348
353
  clearCachedInferences(inferences);
349
354
  inference.isFixed = true;
350
355
  }
351
- return getInferredType(this.c, this.context, i);
356
+ return getInferredType(this.c, this, i);
352
357
  }
353
358
  return type;
354
359
  }
355
360
  }
356
361
 
362
+ /** Maps a context's type parameters to their inferred types without fixing them. */
363
+ export class NonFixingInferenceMapper extends TypeMapper {
364
+ readonly context: InferenceContext;
365
+
366
+ constructor(context: InferenceContext) {
367
+ super();
368
+ this.context = context;
369
+ }
370
+
371
+ map(type: Type): Type {
372
+ const context = this.context;
373
+ const inferences = context.inferences;
374
+ for (let i = 0; i < inferences.length; i++)
375
+ if (type === inferences[i]!.typeParameter) return getInferredType(context.c, context, i);
376
+ return type;
377
+ }
378
+ }
379
+
357
380
  export function createInferenceContext(
358
381
  c: Checker,
359
382
  typeParameters: readonly Type[],
@@ -402,7 +425,7 @@ export function createOuterReturnMapper(c: Checker, context: InferenceContext):
402
425
  }
403
426
 
404
427
  export function addIntraExpressionInferenceSite(context: InferenceContext, node: Node, type: Type): void {
405
- context.intraExpressionInferenceSites.push(new IntraExpressionInferenceSite(node, type));
428
+ (context.intraExpressionInferenceSites ??= []).push(new IntraExpressionInferenceSite(node, type));
406
429
  }
407
430
 
408
431
  export function clearCachedInferences(inferences: InferenceInfo[]): void {
@@ -2126,7 +2149,8 @@ export function instantiateReverseMappedType(c: Checker, t: ReverseMappedType, m
2126
2149
  /** Infers early from intra-expression sites (context-sensitive elements of object and array literals). */
2127
2150
  export function inferFromIntraExpressionSites(c: Checker, context: InferenceContext): void {
2128
2151
  const sites = context.intraExpressionInferenceSites;
2129
- context.intraExpressionInferenceSites = [];
2152
+ if (sites === undefined) return;
2153
+ context.intraExpressionInferenceSites = undefined;
2130
2154
  for (const site of sites) {
2131
2155
  const contextualType = getContextualTypeForInference(c, site.node);
2132
2156
  if (contextualType !== undefined)
@@ -2402,7 +2426,7 @@ export function inferTypesForConditional(
2402
2426
  // The constraints of `infer` type parameters may refer to other roots or to what `mapper` maps,
2403
2427
  // so the non-fixing mapper combines with `mapper`.
2404
2428
  const context = createInferenceContext(c, root.inferTypeParameters, undefined, InferenceFlags.None, undefined);
2405
- if (mapper !== undefined) context.nonFixingMapper = combineTypeMappers(c, context.nonFixingMapper, mapper);
2429
+ if (mapper !== undefined) context.nonFixingOuter = mapper;
2406
2430
  // Inferences from constraints could eagerly resolve what should defer; contravariance is always strict.
2407
2431
  if (!checkTypeDeferred)
2408
2432
  inferTypes(
@@ -1611,6 +1611,170 @@ export function getPropertyOfTypeEx(
1611
1611
  return undefined;
1612
1612
  }
1613
1613
 
1614
+ /**
1615
+ * A long property list that `propertiesRelatedTo` walks: the position of each name, and the positions of
1616
+ * the static private properties, which `getUnmatchedProperty` never reports, and of the required ones.
1617
+ */
1618
+ export class PropertyListIndex {
1619
+ readonly positions: Map<string, number>;
1620
+ readonly staticPrivate: number[];
1621
+ readonly required: number[];
1622
+
1623
+ constructor(positions: Map<string, number>, staticPrivate: number[], required: number[]) {
1624
+ this.positions = positions;
1625
+ this.staticPrivate = staticPrivate;
1626
+ this.required = required;
1627
+ }
1628
+ }
1629
+
1630
+ /** The uses of a long property list after which `propertyListIndex` indexes it by a table of its own. */
1631
+ const propertyListUsesBeforeIndex = 3;
1632
+
1633
+ /**
1634
+ * The index of `properties`, the properties of `target`. Where the members of `target` are properties
1635
+ * with shared names (`PropertyMembers` with `MemberNames`), the index reads the positions of those names.
1636
+ * Other lists get a positions table of their own on the use after the first `propertyListUsesBeforeIndex`:
1637
+ * most long lists are related only a few times, and the lists related many times hold most of the lookups
1638
+ * the index saves. Undefined before that, and where the list repeats a name.
1639
+ */
1640
+ function propertyListIndex(c: Checker, target: Type, properties: Symbol[]): PropertyListIndex | undefined {
1641
+ const known = c.propertyListIndexes.get(properties);
1642
+ if (known instanceof PropertyListIndex) return known;
1643
+ if (known === null) return undefined;
1644
+ let positions: Map<string, number> | undefined;
1645
+ const pending = (target.flags & TypeFlags.Object) !== 0 ? membersHolder(c, target).pendingMembers : undefined;
1646
+ if (
1647
+ pending instanceof PropertyMembers &&
1648
+ pending.materializedProperties === properties &&
1649
+ pending.names !== undefined
1650
+ ) {
1651
+ // The names are the names of the properties, in order (hasMemberNames).
1652
+ positions = pending.names.positions;
1653
+ if (positions.size !== properties.length) {
1654
+ c.propertyListIndexes.set(properties, null);
1655
+ return undefined;
1656
+ }
1657
+ } else {
1658
+ const uses = known ?? 0;
1659
+ if (uses < propertyListUsesBeforeIndex) {
1660
+ c.propertyListIndexes.set(properties, uses + 1);
1661
+ return undefined;
1662
+ }
1663
+ }
1664
+ const own = positions === undefined ? new Map<string, number>() : undefined;
1665
+ const staticPrivate: number[] = [];
1666
+ const required: number[] = [];
1667
+ for (let i = 0; i < properties.length; i++) {
1668
+ const p = properties[i]!;
1669
+ if (own !== undefined) {
1670
+ if (own.has(p.name)) {
1671
+ c.propertyListIndexes.set(properties, null);
1672
+ return undefined;
1673
+ }
1674
+ own.set(p.name, i);
1675
+ }
1676
+ if (isStaticPrivateIdentifierProperty(p)) staticPrivate.push(i);
1677
+ else if ((p.flags & SymbolFlags.Optional) === 0 && (p.checkFlags & CheckFlags.Partial) === 0) required.push(i);
1678
+ }
1679
+ const index = new PropertyListIndex((positions ?? own)!, staticPrivate, required);
1680
+ c.propertyListIndexes.set(properties, index);
1681
+ return index;
1682
+ }
1683
+
1684
+ /**
1685
+ * `getUnmatchedProperty(c, source, target, requireOptionalProperties, false)` where `targetProperties` are
1686
+ * the target's properties and `found` the positions of the names the source has
1687
+ * (`positionsOfNamesFoundInSource`): the first property that is not static private, and is required unless
1688
+ * `requireOptionalProperties`, at a position not in `found`.
1689
+ */
1690
+ export function firstUnmatchedProperty(
1691
+ c: Checker,
1692
+ targetProperties: Symbol[],
1693
+ found: number[],
1694
+ requireOptionalProperties: boolean,
1695
+ ): Symbol | undefined {
1696
+ const index = c.propertyListIndexes.get(targetProperties) as PropertyListIndex;
1697
+ let next = 0;
1698
+ if (!requireOptionalProperties) {
1699
+ for (const position of index.required) {
1700
+ while (next < found.length && found[next]! < position) next++;
1701
+ if (next === found.length || found[next] !== position) return targetProperties[position];
1702
+ }
1703
+ return undefined;
1704
+ }
1705
+ const staticPrivate = index.staticPrivate;
1706
+ let nextPrivate = 0;
1707
+ for (let position = 0; position < targetProperties.length; position++) {
1708
+ // A position can be both found and static private: both lists move past it.
1709
+ const isFound = next < found.length && found[next] === position;
1710
+ if (isFound) next++;
1711
+ const isStaticPrivate = nextPrivate < staticPrivate.length && staticPrivate[nextPrivate] === position;
1712
+ if (isStaticPrivate) nextPrivate++;
1713
+ if (!isFound && !isStaticPrivate) return targetProperties[position];
1714
+ }
1715
+ return undefined;
1716
+ }
1717
+
1718
+ /** The shortest target property list that `positionsOfNamesFoundInSource` indexes. */
1719
+ const indexedTargetPropertiesMinimum = 32;
1720
+
1721
+ /** The properties of `t` where its members are resolved and instantiated, else undefined. */
1722
+ function resolvedPropertiesIfAny(c: Checker, t: Type): Symbol[] | undefined {
1723
+ const holder = (t.objectFlags & ObjectFlags.MembersResolved) !== 0 ? (t as StructuredType) : membersHolder(c, t);
1724
+ if ((holder.objectFlags & ObjectFlags.MembersResolved) === 0) return undefined;
1725
+ const pending = holder.pendingMembers;
1726
+ return pending === undefined || pending.materializedProperties !== undefined ? holder.properties : undefined;
1727
+ }
1728
+
1729
+ /**
1730
+ * The positions in `targetProperties`, the properties of `target`, ascending, of the names
1731
+ * `getPropertyOfType(c, source, name)` finds: the names of the source's properties and of the Function and
1732
+ * Object members its lookups fall back to. `getPropertyOfType` answers undefined for the names at every
1733
+ * other position. Undefined where the answer cannot be read from resolved property lists without resolving
1734
+ * anything (not an object type, an apparent type, members not resolved or not instantiated yet, a module),
1735
+ * where the target list is too short or the source list too long to save lookups, and where the target
1736
+ * list has no index (yet: `propertyListIndex`).
1737
+ */
1738
+ export function positionsOfNamesFoundInSource(
1739
+ c: Checker,
1740
+ source: Type,
1741
+ target: Type,
1742
+ targetProperties: Symbol[],
1743
+ ): number[] | undefined {
1744
+ if (targetProperties.length < indexedTargetPropertiesMinimum) return undefined;
1745
+ if ((source.flags & TypeFlags.Object) === 0 || !isOwnReducedApparentType(source)) return undefined;
1746
+ // A module's member that only an `export type *` declaration exports is not found.
1747
+ if (source.symbol !== undefined && (source.symbol.flags & SymbolFlags.ValueModule) !== 0) return undefined;
1748
+ const sourceProperties = resolvedPropertiesIfAny(c, source);
1749
+ if (sourceProperties === undefined) return undefined;
1750
+ const resolved = membersHolder(c, source);
1751
+ let functionType: Type | undefined;
1752
+ if (source === c.anyFunctionType) functionType = c.globalFunctionType;
1753
+ else if (resolved.callSignatures.length !== 0) functionType = c.globalCallableFunctionType;
1754
+ else if (resolved.constructSignatures.length !== 0) functionType = c.globalNewableFunctionType;
1755
+ let functionProperties = emptySymbols;
1756
+ if (functionType !== undefined && (functionType.flags & TypeFlags.Object) !== 0) {
1757
+ const properties = resolvedPropertiesIfAny(c, functionType);
1758
+ if (properties === undefined) return undefined;
1759
+ functionProperties = properties;
1760
+ }
1761
+ const objectProperties =
1762
+ (c.globalObjectType.flags & TypeFlags.Object) !== 0 ? resolvedPropertiesIfAny(c, c.globalObjectType) : emptySymbols;
1763
+ if (objectProperties === undefined) return undefined;
1764
+ if ((sourceProperties.length + functionProperties.length + objectProperties.length) * 4 > targetProperties.length)
1765
+ return undefined;
1766
+ const index = propertyListIndex(c, target, targetProperties);
1767
+ if (index === undefined) return undefined;
1768
+ const positions = index.positions;
1769
+ const found: number[] = [];
1770
+ for (const list of [sourceProperties, functionProperties, objectProperties])
1771
+ for (const p of list) {
1772
+ const position = positions.get(p.name);
1773
+ if (position !== undefined && !found.includes(position)) found.push(position);
1774
+ }
1775
+ return found.sort((a, b) => a - b);
1776
+ }
1777
+
1614
1778
  export function getPropertyOfObjectType(c: Checker, t: Type, name: string): Symbol | undefined {
1615
1779
  if ((t.flags & TypeFlags.Object) === 0) return undefined;
1616
1780
  const symbol = getResolvedMember(resolveStructuredTypeMembers(c, t), name);
@@ -226,6 +226,22 @@ export function resolveExternalModuleNameWorker(
226
226
  );
227
227
  }
228
228
 
229
+ /** A `resolveExternalModule` result that reported nothing, and the arguments it was computed for. */
230
+ export class ModuleResolution {
231
+ constructor(
232
+ readonly moduleReference: string,
233
+ readonly moduleNotFoundError: DiagnosticMessage | undefined,
234
+ readonly isForAugmentation: boolean,
235
+ readonly symbol: Symbol | undefined,
236
+ ) {}
237
+ }
238
+
239
+ /**
240
+ * The module symbol `moduleReference` resolves to from `location`, reporting resolution errors at
241
+ * `errorNode`. Each name an import or export declaration lists resolves its module specifier again,
242
+ * with the same result and the same diagnostics: a result that reported nothing is kept for the
243
+ * specifier and returned to later calls from the same declaration (`sharesSpecifierContext`).
244
+ */
229
245
  export function resolveExternalModule(
230
246
  c: Checker,
231
247
  location: Node,
@@ -233,6 +249,80 @@ export function resolveExternalModule(
233
249
  moduleNotFoundError: DiagnosticMessage | undefined,
234
250
  errorNode: Node | undefined,
235
251
  isForAugmentation: boolean,
252
+ ): Symbol | undefined {
253
+ // An augmentation merged later can give the resolved module a new merged symbol, so results are
254
+ // kept only once every augmentation is merged.
255
+ const shared = c.modulesMerged && errorNode !== undefined && sharesSpecifierContext(location, errorNode);
256
+ if (shared) {
257
+ const known = c.moduleResolutions.get(errorNode);
258
+ if (
259
+ known !== undefined &&
260
+ known.moduleReference === moduleReference &&
261
+ known.moduleNotFoundError === moduleNotFoundError &&
262
+ known.isForAugmentation === isForAugmentation
263
+ )
264
+ return known.symbol;
265
+ }
266
+ const reports = c.reports;
267
+ const symbol = resolveExternalModuleWorker(
268
+ c,
269
+ location,
270
+ moduleReference,
271
+ moduleNotFoundError,
272
+ errorNode,
273
+ isForAugmentation,
274
+ );
275
+ if (shared && c.reports === reports)
276
+ c.moduleResolutions.set(
277
+ errorNode,
278
+ new ModuleResolution(moduleReference, moduleNotFoundError, isForAugmentation, symbol),
279
+ );
280
+ return symbol;
281
+ }
282
+
283
+ /**
284
+ * Whether a resolution from `location` with errors at `errorNode` depends on `location` only through
285
+ * the declaration that `errorNode` is the module specifier of: `location` is that declaration, a
286
+ * clause or specifier inside it, or the specifier itself. The resolution mode, the emittability of
287
+ * the import and its resolution-mode attributes then all come from that declaration.
288
+ */
289
+ function sharesSpecifierContext(location: Node, errorNode: Node): boolean {
290
+ const declaration = errorNode.parent;
291
+ if (
292
+ declaration === undefined ||
293
+ (declaration.kind !== SyntaxKind.ImportDeclaration &&
294
+ declaration.kind !== SyntaxKind.JSImportDeclaration &&
295
+ declaration.kind !== SyntaxKind.ExportDeclaration) ||
296
+ getModuleSpecifier(declaration) !== errorNode
297
+ )
298
+ return false;
299
+ for (let current: Node | undefined = location; current !== undefined; current = current.parent)
300
+ switch (current.kind) {
301
+ case SyntaxKind.ImportDeclaration:
302
+ case SyntaxKind.JSImportDeclaration:
303
+ case SyntaxKind.ExportDeclaration:
304
+ return current === declaration;
305
+ case SyntaxKind.ImportClause:
306
+ case SyntaxKind.NamedImports:
307
+ case SyntaxKind.NamespaceImport:
308
+ case SyntaxKind.ImportSpecifier:
309
+ case SyntaxKind.NamedExports:
310
+ case SyntaxKind.NamespaceExport:
311
+ case SyntaxKind.ExportSpecifier:
312
+ break;
313
+ default:
314
+ if (current !== errorNode) return false;
315
+ }
316
+ return false;
317
+ }
318
+
319
+ function resolveExternalModuleWorker(
320
+ c: Checker,
321
+ location: Node,
322
+ moduleReference: string,
323
+ moduleNotFoundError: DiagnosticMessage | undefined,
324
+ errorNode: Node | undefined,
325
+ isForAugmentation: boolean,
236
326
  ): Symbol | undefined {
237
327
  if (errorNode !== undefined && moduleReference.startsWith('@types/'))
238
328
  c.error(
@@ -789,7 +879,8 @@ function isOnlyImportableAsDefault(c: Checker, usage: Node, resolvedModule: Symb
789
879
  const targetFile = moduleSymbol !== undefined ? getSourceFileOfModuleSymbol(moduleSymbol) : undefined;
790
880
  return (
791
881
  targetFile !== undefined &&
792
- (isJsonSourceFile(targetFile) || getDeclarationFileExtension(targetFile.fileName) === '.d.json.ts')
882
+ (isJsonSourceFile(targetFile) ||
883
+ (targetFile.fileName.endsWith('.d.json.ts') && getDeclarationFileExtension(targetFile.fileName) === '.d.json.ts'))
793
884
  );
794
885
  }
795
886
 
@@ -72,6 +72,7 @@ import {
72
72
  type TypeChecking,
73
73
  } from './checker-types.js';
74
74
  import { getBaseTypes } from './checker-declared-types.js';
75
+ import type { ModuleResolution } from './checker-modules.js';
75
76
  import { getFalseTypeFromConditionalType, getTrueTypeFromConditionalType } from './checker-type-operators.js';
76
77
  import {
77
78
  createArrayType,
@@ -112,6 +113,7 @@ import { SourceFileMetadata, getEmitModuleFormatOfFile } from './program-utiliti
112
113
  import type { GrammarChecker } from './checker-grammar.js';
113
114
  import type { ResolvedModule } from './module-resolution-types.js';
114
115
  import type { ModuleSpecifierHost } from './module-specifiers.js';
116
+ import type { PropertyListIndex } from './checker-members.js';
115
117
 
116
118
  /** What the checker needs from a program. */
117
119
  export abstract class CheckerHost {
@@ -345,6 +347,15 @@ export class Checker implements TypeChecking {
345
347
 
346
348
  readonly diagnostics = new DiagnosticCollection();
347
349
  readonly suggestionDiagnostics = new DiagnosticCollection();
350
+ /** Diagnostics and suggestions reported so far, whether kept or not (`resolveExternalModule`'s cache). */
351
+ reports = 0;
352
+ /** `resolveExternalModule` results that reported nothing, by module specifier (checker-modules.ts). */
353
+ readonly moduleResolutions = new Map<Node, ModuleResolution>();
354
+ /**
355
+ * Whether ambient modules and module augmentations are merged. Merging can give a module a new
356
+ * merged symbol, so `resolveExternalModule` keeps results only from then on.
357
+ */
358
+ modulesMerged = false;
348
359
  /** Module specifiers of `import("…")` types, by module symbol id, importing file path and resolution mode. */
349
360
  readonly moduleSpecifiers = new Map<string, string>();
350
361
  /** Specifier generation for hosts without a program. */
@@ -359,6 +370,11 @@ export class Checker implements TypeChecking {
359
370
  readonly sideHolders = new LinkTable<Type>();
360
371
  /** The same for signatures with side state (`signatureSideOf`), by signature id. */
361
372
  readonly signatureSideHolders = new LinkTable<Signature>();
373
+ /**
374
+ * The indexes of long property lists that `propertiesRelatedTo` walks; before a list is indexed, its uses
375
+ * so far; null where a list repeats a name.
376
+ */
377
+ readonly propertyListIndexes = new Map<Symbol[], PropertyListIndex | number | null>();
362
378
  /** The type of a value symbol. */
363
379
  readonly symbolTypes = new LinkTable<Type>();
364
380
  /** For instantiated and synthetic symbols, the symbol they come from. */
@@ -894,6 +910,7 @@ export class Checker implements TypeChecking {
894
910
  this.diagnostics.startLog();
895
911
  this.suggestionDiagnostics.startLog();
896
912
  mergeAmbientModulesAndAugmentations(this);
913
+ this.modulesMerged = true;
897
914
  this.mergeDiagnostics.push(this.diagnostics.takeLog(), this.suggestionDiagnostics.takeLog());
898
915
  }
899
916
 
@@ -1026,11 +1043,13 @@ export class Checker implements TypeChecking {
1026
1043
  }
1027
1044
 
1028
1045
  addDiagnostic(diagnostic: Diagnostic): void {
1046
+ this.reports++;
1029
1047
  // Discard diagnostics created while at the maximum number of nested typeToString calls.
1030
1048
  if (this.serializationLevel < maxSerializationLevel) this.diagnostics.add(diagnostic);
1031
1049
  }
1032
1050
 
1033
1051
  addSuggestion(diagnostic: Diagnostic): void {
1052
+ this.reports++;
1034
1053
  this.suggestionDiagnostics.add(diagnostic);
1035
1054
  }
1036
1055
 
@@ -13,7 +13,6 @@ import {
13
13
  BindingPattern,
14
14
  Block,
15
15
  BreakStatement,
16
- CallExpression,
17
16
  CallSignatureDeclaration,
18
17
  CaseBlock,
19
18
  CaseOrDefaultClause,
@@ -149,7 +148,6 @@ import {
149
148
  PrefixUnaryExpression,
150
149
  PrivateIdentifier,
151
150
  PropertyAccessExpression,
152
- PropertyAssignment,
153
151
  PropertyDeclaration,
154
152
  PropertySignatureDeclaration,
155
153
  QualifiedName,
@@ -197,6 +195,8 @@ import {
197
195
  WhileStatement,
198
196
  WithStatement,
199
197
  YieldExpression,
198
+ newCallExpression,
199
+ newPropertyAssignment,
200
200
  } from './ast.js';
201
201
  import { modifiersToFlags } from './ast-utilities.js';
202
202
  import { protocolVersion } from './encoder.js';
@@ -883,7 +883,7 @@ class Decoder {
883
883
  case SyntaxKind.ElementAccessExpression:
884
884
  return new ElementAccessExpression(this.required(0), this.token(1), this.required(2), 0);
885
885
  case SyntaxKind.CallExpression:
886
- return new CallExpression(this.required(0), this.token(1), this.list(2), this.requiredList(3), 0);
886
+ return newCallExpression(this.required(0), this.token(1), this.list(2), this.requiredList(3), 0, false);
887
887
  case SyntaxKind.NewExpression:
888
888
  return new NewExpression(this.required(0), this.list(1), this.list(2));
889
889
  case SyntaxKind.MetaProperty:
@@ -910,12 +910,13 @@ class Decoder {
910
910
  case SyntaxKind.SpreadAssignment:
911
911
  return new SpreadAssignment(this.required(0));
912
912
  case SyntaxKind.PropertyAssignment:
913
- return new PropertyAssignment(
913
+ return newPropertyAssignment(
914
914
  this.modifiers(0),
915
915
  this.required(1),
916
916
  this.token(2),
917
917
  this.node(3),
918
918
  this.required(4),
919
+ false,
919
920
  );
920
921
  case SyntaxKind.ShorthandPropertyAssignment:
921
922
  return new ShorthandPropertyAssignment(
@@ -174,12 +174,15 @@ function threadCopyOf(file: DiagnosticFile): DiagnosticFile | undefined {
174
174
  return copy;
175
175
  }
176
176
 
177
+ /** The empty value of a file's position tables until they are computed. */
178
+ const noNumbers: readonly number[] = [];
179
+
177
180
  /** The text of a file diagnostics point into. */
178
181
  export class DiagnosticFile {
179
182
  readonly fileName: string;
180
183
  readonly text: string;
181
184
  // Computed on first use; never empty once computed.
182
- private lineStarts: number[] = [];
185
+ private lineStarts: readonly number[] = noNumbers;
183
186
  /** Whether this is a thread's copy of a possibly shared file (`threadCopyOf`). */
184
187
  isThreadCopy = false;
185
188
 
@@ -210,8 +213,8 @@ export class DiagnosticFile {
210
213
  // The non-ASCII characters, in order: their UTF-16 offsets, UTF-8 lengths, and the UTF-8 bytes
211
214
  // beyond one per UTF-16 code unit of the text up to and including each. Unset until first used.
212
215
  private nonASCIIStarts: number[] | undefined = undefined;
213
- private nonASCIILengths: number[] = [];
214
- private extraBytes: number[] = [];
216
+ private nonASCIILengths: readonly number[] = noNumbers;
217
+ private extraBytes: readonly number[] = noNumbers;
215
218
 
216
219
  private getNonASCIIStarts(): number[] {
217
220
  let starts = this.nonASCIIStarts;
@@ -221,8 +224,8 @@ export class DiagnosticFile {
221
224
  if (Buffer.byteLength(text, 'utf8') !== text.length) {
222
225
  // Scanning the UTF-8 encoding is faster than reading each code unit.
223
226
  const bytes = Buffer.from(text, 'utf8');
224
- const lengths = this.nonASCIILengths;
225
- const extraBytes = this.extraBytes;
227
+ const lengths: number[] = [];
228
+ const extraBytes: number[] = [];
226
229
  let extra = 0;
227
230
  for (let i = 0; i < bytes.length; i++) {
228
231
  const b = bytes[i]!;
@@ -235,6 +238,8 @@ export class DiagnosticFile {
235
238
  extraBytes.push(extra);
236
239
  i += length - 1;
237
240
  }
241
+ this.nonASCIILengths = lengths;
242
+ this.extraBytes = extraBytes;
238
243
  }
239
244
  this.nonASCIIStarts = starts;
240
245
  }