From 8a80853690c6defc11bd92862b534284e879858f Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 29 Oct 2026 13:59:13 -0700 Subject: [PATCH 1/3] checker api --- packages/typescript/src/api/async/api.ts | 127 ++++++ .../typescript/src/api/proto.generated.ts | 104 ++++- packages/typescript/src/api/proto.ts | 2 +- packages/typescript/src/api/sync/api.ts | 338 +++++++++++++++ packages/typescript/test/async/api.test.ts | 386 ++++++++++++++++++ .../test/sync/api-generators.test.ts | 3 + packages/typescript/test/sync/api.test.ts | 386 ++++++++++++++++++ tools/scripts/gen/apidiff.mts | 295 +++++++++++++ tools/scripts/gen/apiold.mts | 104 +++++ tsc/internal/api/proto.go | 346 +++++++++------- tsc/internal/api/session.go | 235 +++++++++++ tsc/internal/checker/exports.go | 36 ++ 12 files changed, 2207 insertions(+), 155 deletions(-) create mode 100644 tools/scripts/gen/apidiff.mts create mode 100644 tools/scripts/gen/apiold.mts diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index 29369a7392809..e7e20c2218681 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -2623,6 +2623,20 @@ export class Checker { return type.getNonNullableType(); } + /** + * Add `undefined` and/or `null` to a type, as indicated by `flags` (a combination of + * {@link TypeFlags.Undefined} and {@link TypeFlags.Null}). Always returns a type. + */ + async getNullableType(type: Type, flags: TypeFlags): Promise { + const data = await this.client.apiRequest("getNullableType", { + snapshot: this.snapshotId, + project: this.project.handle, + type: type.id, + flags, + }); + return this.objectRegistry.getOrCreateType(data); + } + /** * Get the type for a type node. Always returns a type; for type nodes whose * type cannot be determined the checker yields the error type (use @@ -2746,6 +2760,84 @@ export class Checker { async getNonPrimitiveType(): Promise { return this.getIntrinsicType("getNonPrimitiveType"); } + async getFalseType(): Promise { + return this.getIntrinsicType("getFalseType"); + } + async getTrueType(): Promise { + return this.getIntrinsicType("getTrueType"); + } + + /** Get (or create) the singleton type for a string literal value. Always returns a type. */ + async getStringLiteralType(value: string): Promise { + const data = await this.client.apiRequest("getStringLiteralType", { + snapshot: this.snapshotId, + project: this.project.handle, + value, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** Get (or create) the singleton type for a numeric literal value. Always returns a type. */ + async getNumberLiteralType(value: number): Promise { + const data = await this.client.apiRequest("getNumberLiteralType", { + snapshot: this.snapshotId, + project: this.project.handle, + value, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** Get (or create) the singleton type for a bigint literal value. Always returns a type. */ + async getBigIntLiteralType(value: bigint): Promise { + const negative = value < 0n; + const absValue = negative ? -value : value; + const base10Value = absValue === 0n ? "" : absValue.toString(); + const data = await this.client.apiRequest("getBigIntLiteralType", { + snapshot: this.snapshotId, + project: this.project.handle, + negative, + base10Value, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** + * Get the augmented properties of a type: its properties, plus (when the type has call or + * construct signatures) the properties of the global `CallableFunction`/`NewableFunction` + * interfaces not already present. + */ + async getAugmentedPropertiesOfType(type: Type): Promise { + const data = await this.client.apiRequest("getAugmentedPropertiesOfType", { + snapshot: this.snapshotId, + project: this.project.handle, + objectId: type.id, + }); + return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; + } + + async getJsxIntrinsicTagNamesAt(location: Node): Promise { + const data = await this.client.apiRequest("getJsxIntrinsicTagNamesAt", { + snapshot: this.snapshotId, + project: this.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; + } + + /** + * Look up a private identifier (e.g. `"#foo"`, including the leading `#`) lexically in scope + * at `location`, and if found, return the corresponding property of `type`. + */ + async getPrivateIdentifierPropertyOfType(type: Type, name: string, location: Node): Promise { + const data = await this.client.apiRequest("getPrivateIdentifierPropertyOfType", { + snapshot: this.snapshotId, + project: this.project.handle, + type: type.id, + name, + location: getNodeRequestHandle(location), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } async typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: number): Promise { const binaryData = await this.client.apiRequestBinary("typeToTypeNode", { @@ -2825,6 +2917,41 @@ export class Checker { }); } + /** + * Returns `true` if the function-like declaration is the implementation + * signature of a set of overloads (i.e. it has a body and is not a + * get/set accessor). + */ + async isImplementationOfOverload(node: Node): Promise { + return this.client.apiRequest("isImplementationOfOverload", { + snapshot: this.snapshotId, + project: this.project.handle, + location: getNodeRequestHandle(node), + }); + } + + /** + * Get the property symbol corresponding to an identifier in a destructuring + * assignment or `for..of` statement, e.g. `a` in `({ property1: a } = obj)`. + */ + async getPropertySymbolOfDestructuringAssignment(location: Identifier): Promise { + const data = await this.client.apiRequest("getPropertySymbolOfDestructuringAssignment", { + snapshot: this.snapshotId, + project: this.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** Get all ambient modules declared in the program (e.g. `declare module "foo" { ... }`). */ + async getAmbientModules(): Promise { + const data = await this.client.apiRequest("getAmbientModules", { + snapshot: this.snapshotId, + project: this.project.handle, + }); + return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; + } + /** Get the return type of a signature. Always returns a type. */ async getReturnTypeOfSignature(signature: Signature): Promise { return signature.getReturnType(); diff --git a/packages/typescript/src/api/proto.generated.ts b/packages/typescript/src/api/proto.generated.ts index 51b1123b15634..9e95049747137 100644 --- a/packages/typescript/src/api/proto.generated.ts +++ b/packages/typescript/src/api/proto.generated.ts @@ -131,6 +131,15 @@ export interface APIMethodInfo { getAwaitedType: APIMethod; getBaseTypeOfLiteralType: APIMethod; getNonNullableType: APIMethod; + getNullableType: APIMethod; + getStringLiteralType: APIMethod; + getNumberLiteralType: APIMethod; + getBigIntLiteralType: APIMethod; + getFalseType: APIMethod; + getTrueType: APIMethod; + getAugmentedPropertiesOfType: APIMethod; + getJsxIntrinsicTagNamesAt: APIMethod; + getPrivateIdentifierPropertyOfType: APIMethod; getTypeFromTypeNode: APIMethod; getWidenedType: APIMethod; getParameterType: APIMethod; @@ -180,6 +189,9 @@ export interface APIMethodInfo { getDocumentationComment: APIMethod; isArrayType: APIMethod; isReadonlySymbol: APIMethod; + isImplementationOfOverload: APIMethod; + getPropertySymbolOfDestructuringAssignment: APIMethod; + getAmbientModules: APIMethod; getReferencesToSymbolInFile: APIMethod; getReferencedSymbolsForNode: APIMethod; getSignatureUsages: APIMethod; @@ -799,6 +811,61 @@ export interface GetBaseTypeOfLiteralTypeParams { type: number; } +/** GetNullableTypeParams are the parameters for the getNullableType method. */ +export interface GetNullableTypeParams { + snapshot: number; + project: number; + type: number; + flags: number; +} + +/** GetStringLiteralTypeParams are the parameters for the getStringLiteralType method. */ +export interface GetStringLiteralTypeParams { + snapshot: number; + project: number; + value: string; +} + +/** GetNumberLiteralTypeParams are the parameters for the getNumberLiteralType method. */ +export interface GetNumberLiteralTypeParams { + snapshot: number; + project: number; + value: number; +} + +/** + * GetBigIntLiteralTypeParams are the parameters for the getBigIntLiteralType method. + * Base10Value is the absolute value in base 10 with no leading zeros (empty for zero). + */ +export interface GetBigIntLiteralTypeParams { + snapshot: number; + project: number; + negative?: boolean | undefined; + base10Value?: string | undefined; +} + +/** GetIntrinsicTypeParams is used for intrinsic type getters (anyType, stringType, etc.). */ +export interface GetIntrinsicTypeParams { + snapshot: number; + project: number; +} + +/** CheckerNodeParams are parameters for checker methods that operate on a node location. */ +export interface CheckerNodeParams { + snapshot: number; + project: number; + location: string; +} + +/** GetPrivateIdentifierPropertyOfTypeParams are the parameters for the getPrivateIdentifierPropertyOfType method. */ +export interface GetPrivateIdentifierPropertyOfTypeParams { + snapshot: number; + project: number; + type: number; + name: string; + location: string; +} + /** GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. */ export interface GetTypeFromTypeNodeParams { snapshot: number; @@ -914,13 +981,6 @@ export interface TextEdit { newText: string; } -/** CheckerNodeParams are parameters for checker methods that operate on a node location. */ -export interface CheckerNodeParams { - snapshot: number; - project: number; - location: string; -} - export interface ConstantValueResponse { isNumber: boolean; value: unknown; @@ -1091,12 +1151,6 @@ export interface SelectedFilesEmitParams { files: readonly DocumentIdentifier[] | null; } -/** GetIntrinsicTypeParams is used for intrinsic type getters (anyType, stringType, etc.). */ -export interface GetIntrinsicTypeParams { - snapshot: number; - project: number; -} - /** * WellKnownSymbolsResponse carries the handle ids of the per-checker singleton * symbols (unknown, undefined, arguments) so the client can identify them by id @@ -1169,14 +1223,17 @@ export interface BatchRequest { | "getAliasSymbolOfType" | "getAliasTypeArgumentsOfType" | "getAliasedSymbol" + | "getAmbientModules" | "getAnyType" | "getApparentPropertiesOfType" | "getApparentType" + | "getAugmentedPropertiesOfType" | "getAwaitedType" | "getBaseConstraintOfType" | "getBaseTypeOfLiteralType" | "getBaseTypeOfType" | "getBaseTypes" + | "getBigIntLiteralType" | "getBigIntType" | "getBindDiagnostics" | "getBooleanType" @@ -1206,6 +1263,7 @@ export interface BatchRequest { | "getExportsOfModule" | "getExportsOfSymbol" | "getExtendsTypeOfType" + | "getFalseType" | "getFalseTypeOfConditionalType" | "getFreshTypeOfType" | "getFullyQualifiedName" @@ -1217,6 +1275,7 @@ export interface BatchRequest { | "getIndexTypeOfType" | "getJavaScriptEmit" | "getJsDocTags" + | "getJsxIntrinsicTagNamesAt" | "getLocalTypeParametersOfType" | "getMemberInModuleExports" | "getMembersOfSymbol" @@ -1229,6 +1288,8 @@ export interface BatchRequest { | "getNonNullableType" | "getNonPrimitiveType" | "getNullType" + | "getNullableType" + | "getNumberLiteralType" | "getNumberType" | "getObjectTypeOfType" | "getOuterTypeParametersOfType" @@ -1236,9 +1297,11 @@ export interface BatchRequest { | "getParametersOfSignature" | "getParentOfSymbol" | "getParentOfSymbolForChecker" + | "getPrivateIdentifierPropertyOfType" | "getProgramDiagnostics" | "getPropertiesOfType" | "getPropertyOfType" + | "getPropertySymbolOfDestructuringAssignment" | "getReducedType" | "getReferencedSymbolsForNode" | "getReferencesToSymbolInFile" @@ -1258,6 +1321,7 @@ export interface BatchRequest { | "getSourceFile" | "getSourceFileMetadata" | "getSourceFileNames" + | "getStringLiteralType" | "getStringType" | "getSuggestionDiagnostics" | "getSymbolAtLocation" @@ -1278,6 +1342,7 @@ export interface BatchRequest { | "getTemplateTypeOfMappedType" | "getThisParameterOfSignature" | "getThisTypeOfType" + | "getTrueType" | "getTrueTypeOfConditionalType" | "getTypeArguments" | "getTypeAtLocation" @@ -1305,6 +1370,7 @@ export interface BatchRequest { | "isArrayLikeType" | "isArrayType" | "isContextSensitive" + | "isImplementationOfOverload" | "isReadonlySymbol" | "isTypeAssignableTo" | "parseCommandLine" @@ -1351,14 +1417,17 @@ export interface BatchResponse { | "getAliasSymbolOfType" | "getAliasTypeArgumentsOfType" | "getAliasedSymbol" + | "getAmbientModules" | "getAnyType" | "getApparentPropertiesOfType" | "getApparentType" + | "getAugmentedPropertiesOfType" | "getAwaitedType" | "getBaseConstraintOfType" | "getBaseTypeOfLiteralType" | "getBaseTypeOfType" | "getBaseTypes" + | "getBigIntLiteralType" | "getBigIntType" | "getBindDiagnostics" | "getBooleanType" @@ -1388,6 +1457,7 @@ export interface BatchResponse { | "getExportsOfModule" | "getExportsOfSymbol" | "getExtendsTypeOfType" + | "getFalseType" | "getFalseTypeOfConditionalType" | "getFreshTypeOfType" | "getFullyQualifiedName" @@ -1399,6 +1469,7 @@ export interface BatchResponse { | "getIndexTypeOfType" | "getJavaScriptEmit" | "getJsDocTags" + | "getJsxIntrinsicTagNamesAt" | "getLocalTypeParametersOfType" | "getMemberInModuleExports" | "getMembersOfSymbol" @@ -1411,6 +1482,8 @@ export interface BatchResponse { | "getNonNullableType" | "getNonPrimitiveType" | "getNullType" + | "getNullableType" + | "getNumberLiteralType" | "getNumberType" | "getObjectTypeOfType" | "getOuterTypeParametersOfType" @@ -1418,9 +1491,11 @@ export interface BatchResponse { | "getParametersOfSignature" | "getParentOfSymbol" | "getParentOfSymbolForChecker" + | "getPrivateIdentifierPropertyOfType" | "getProgramDiagnostics" | "getPropertiesOfType" | "getPropertyOfType" + | "getPropertySymbolOfDestructuringAssignment" | "getReducedType" | "getReferencedSymbolsForNode" | "getReferencesToSymbolInFile" @@ -1440,6 +1515,7 @@ export interface BatchResponse { | "getSourceFile" | "getSourceFileMetadata" | "getSourceFileNames" + | "getStringLiteralType" | "getStringType" | "getSuggestionDiagnostics" | "getSymbolAtLocation" @@ -1460,6 +1536,7 @@ export interface BatchResponse { | "getTemplateTypeOfMappedType" | "getThisParameterOfSignature" | "getThisTypeOfType" + | "getTrueType" | "getTrueTypeOfConditionalType" | "getTypeArguments" | "getTypeAtLocation" @@ -1487,6 +1564,7 @@ export interface BatchResponse { | "isArrayLikeType" | "isArrayType" | "isContextSensitive" + | "isImplementationOfOverload" | "isReadonlySymbol" | "isTypeAssignableTo" | "parseCommandLine" diff --git a/packages/typescript/src/api/proto.ts b/packages/typescript/src/api/proto.ts index 322b3ff4b2bb8..c3093985c1d60 100644 --- a/packages/typescript/src/api/proto.ts +++ b/packages/typescript/src/api/proto.ts @@ -69,7 +69,7 @@ export type SymbolsPropertyMethod = APIMethodsReturning; export type TypePropertyMethod = Exclude, IntrinsicTypeMethod>; export type TypesPropertyMethod = APIMethodsReturning; -export type IntrinsicTypeMethod = "getAnyType" | "getBigIntType" | "getBooleanType" | "getESSymbolType" | "getNeverType" | "getNonPrimitiveType" | "getNullType" | "getNumberType" | "getStringType" | "getUndefinedType" | "getUnknownType" | "getVoidType"; +export type IntrinsicTypeMethod = "getAnyType" | "getBigIntType" | "getBooleanType" | "getESSymbolType" | "getFalseType" | "getNeverType" | "getNonPrimitiveType" | "getNullType" | "getNumberType" | "getStringType" | "getTrueType" | "getUndefinedType" | "getUnknownType" | "getVoidType"; type BatchableAPIMethod = Exclude; export type APIRequest = { [K in BatchableAPIMethod]: { method: K; params: APIMethodInfo[K]["params"]; }; }[BatchableAPIMethod]; diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index e271dafabaf7b..a532bcad9cddc 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -4954,6 +4954,39 @@ export class Checker { ); } + /** + * Add `undefined` and/or `null` to a type, as indicated by `flags` (a combination of + * {@link TypeFlags.Undefined} and {@link TypeFlags.Null}). Always returns a type. + */ + get getNullableType(): { + (type: Type, flags: TypeFlags): Type; + gen(type: Type, flags: TypeFlags): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNullableType", + function (type: Type, flags: TypeFlags): Type { + const data = owner.client.apiRequest("getNullableType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + type: type.id, + flags, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (type: Type, flags: TypeFlags): Generator { + const data = yield* apiRequest("getNullableType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + type: type.id, + flags, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + /** * Get the type for a type node. Always returns a type; for type nodes whose * type cannot be determined the checker yields the error type (use @@ -5377,6 +5410,224 @@ export class Checker { }, ); } + get getFalseType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getFalseType", + function (): Type { + return owner.getIntrinsicType("getFalseType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getFalseType"); + }, + ); + } + get getTrueType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTrueType", + function (): Type { + return owner.getIntrinsicType("getTrueType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getTrueType"); + }, + ); + } + + /** Get (or create) the singleton type for a string literal value. Always returns a type. */ + get getStringLiteralType(): { + (value: string): Type; + gen(value: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getStringLiteralType", + function (value: string): Type { + const data = owner.client.apiRequest("getStringLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (value: string): Generator { + const data = yield* apiRequest("getStringLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** Get (or create) the singleton type for a numeric literal value. Always returns a type. */ + get getNumberLiteralType(): { + (value: number): Type; + gen(value: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNumberLiteralType", + function (value: number): Type { + const data = owner.client.apiRequest("getNumberLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (value: number): Generator { + const data = yield* apiRequest("getNumberLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** Get (or create) the singleton type for a bigint literal value. Always returns a type. */ + get getBigIntLiteralType(): { + (value: bigint): Type; + gen(value: bigint): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBigIntLiteralType", + function (value: bigint): Type { + const negative = value < 0n; + const absValue = negative ? -value : value; + const base10Value = absValue === 0n ? "" : absValue.toString(); + const data = owner.client.apiRequest("getBigIntLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + negative, + base10Value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (value: bigint): Generator { + const negative = value < 0n; + const absValue = negative ? -value : value; + const base10Value = absValue === 0n ? "" : absValue.toString(); + const data = yield* apiRequest("getBigIntLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + negative, + base10Value, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** + * Get the augmented properties of a type: its properties, plus (when the type has call or + * construct signatures) the properties of the global `CallableFunction`/`NewableFunction` + * interfaces not already present. + */ + get getAugmentedPropertiesOfType(): { + (type: Type): readonly Symbol[]; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAugmentedPropertiesOfType", + function (type: Type): readonly Symbol[] { + const data = owner.client.apiRequest("getAugmentedPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + objectId: type.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + function* (type: Type): Generator { + const data = yield* apiRequest("getAugmentedPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + objectId: type.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + ); + } + + get getJsxIntrinsicTagNamesAt(): { + (location: Node): readonly Symbol[]; + gen(location: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getJsxIntrinsicTagNamesAt", + function (location: Node): readonly Symbol[] { + const data = owner.client.apiRequest("getJsxIntrinsicTagNamesAt", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + function* (location: Node): Generator { + const data = yield* apiRequest("getJsxIntrinsicTagNamesAt", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + ); + } + + /** + * Look up a private identifier (e.g. `"#foo"`, including the leading `#`) lexically in scope + * at `location`, and if found, return the corresponding property of `type`. + */ + get getPrivateIdentifierPropertyOfType(): { + (type: Type, name: string, location: Node): Symbol | undefined; + gen(type: Type, name: string, location: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getPrivateIdentifierPropertyOfType", + function (type: Type, name: string, location: Node): Symbol | undefined { + const data = owner.client.apiRequest("getPrivateIdentifierPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + type: type.id, + name, + location: getNodeRequestHandle(location), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (type: Type, name: string, location: Node): Generator { + const data = yield* apiRequest("getPrivateIdentifierPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.handle, + type: type.id, + name, + location: getNodeRequestHandle(location), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } get typeToTypeNode(): { (type: Type, enclosingDeclaration?: Node, flags?: number): TypeNode | undefined; @@ -5601,6 +5852,93 @@ export class Checker { ); } + /** + * Returns `true` if the function-like declaration is the implementation + * signature of a set of overloads (i.e. it has a body and is not a + * get/set accessor). + */ + get isImplementationOfOverload(): { + (node: Node): boolean; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isImplementationOfOverload", + function (node: Node): boolean { + return owner.client.apiRequest("isImplementationOfOverload", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(node), + }); + }, + function* (node: Node): Generator { + return yield* apiRequest("isImplementationOfOverload", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(node), + }); + }, + ); + } + + /** + * Get the property symbol corresponding to an identifier in a destructuring + * assignment or `for..of` statement, e.g. `a` in `({ property1: a } = obj)`. + */ + get getPropertySymbolOfDestructuringAssignment(): { + (location: Identifier): Symbol | undefined; + gen(location: Identifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getPropertySymbolOfDestructuringAssignment", + function (location: Identifier): Symbol | undefined { + const data = owner.client.apiRequest("getPropertySymbolOfDestructuringAssignment", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (location: Identifier): Generator { + const data = yield* apiRequest("getPropertySymbolOfDestructuringAssignment", { + snapshot: owner.snapshotId, + project: owner.project.handle, + location: getNodeRequestHandle(location), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + /** Get all ambient modules declared in the program (e.g. `declare module "foo" { ... }`). */ + get getAmbientModules(): { + (): readonly Symbol[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAmbientModules", + function (): readonly Symbol[] { + const data = owner.client.apiRequest("getAmbientModules", { + snapshot: owner.snapshotId, + project: owner.project.handle, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + function* (): Generator { + const data = yield* apiRequest("getAmbientModules", { + snapshot: owner.snapshotId, + project: owner.project.handle, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + ); + } + /** Get the return type of a signature. Always returns a type. */ get getReturnTypeOfSignature(): { (signature: Signature): Type; diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index 9a37f0c38dfff..fae02de58bf64 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -7,6 +7,7 @@ import { getJSDocTags, getSynthesizedDeepClone, getTextOfJSDocComment, + type Identifier, InternalSymbolName, isCallExpression, isClassDeclaration, @@ -17,9 +18,12 @@ import { isInterfaceDeclaration, isJSDoc, isJSDocParameterTag, + isJsxSelfClosingElement, isModuleDeclaration, isNamedImports, isObjectLiteralExpression, + isPrivateIdentifier, + isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isReturnStatement, @@ -4839,6 +4843,106 @@ describe("Checker - intrinsic type getters", { concurrency }, () => { }); }); +describe("Checker - literal and nullable type getters", { concurrency }, () => { + const intrinsicFiles = { + "/tsconfig.json": "{}", + "/src/main.ts": `export const x = 1;`, + }; + + test("getStringLiteralType returns a StringLiteral type with the given value", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const type = await project.checker.getStringLiteralType("hello"); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral); + assert.equal((type as LiteralType).value, "hello"); + + // The checker caches literal types, so asking for the same value twice should + // yield the same underlying type. + const type2 = await project.checker.getStringLiteralType("hello"); + assert.equal(type2.id, type.id); + }); + + test("getNumberLiteralType returns a NumberLiteral type with the given value", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const type = await project.checker.getNumberLiteralType(42); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + assert.equal((type as NumberLiteralType).value, 42); + + const negType = await project.checker.getNumberLiteralType(-1.5); + assert.ok(negType.flags & TypeFlags.NumberLiteral); + assert.equal((negType as NumberLiteralType).value, -1.5); + }); + + test("getBigIntLiteralType returns a BigIntLiteral type with the given value (positive, negative, zero)", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const posType = await project.checker.getBigIntLiteralType(123n); + assert.ok(posType.flags & TypeFlags.BigIntLiteral); + assert.equal((posType as BigIntLiteralType).value, 123n); + + const negType = await project.checker.getBigIntLiteralType(-123n); + assert.ok(negType.flags & TypeFlags.BigIntLiteral); + assert.equal((negType as BigIntLiteralType).value, -123n); + + const zeroType = await project.checker.getBigIntLiteralType(0n); + assert.ok(zeroType.flags & TypeFlags.BigIntLiteral); + assert.equal((zeroType as BigIntLiteralType).value, 0n); + }); + + test("getNullableType adds Undefined and/or Null to a type", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const stringType = await project.checker.getStringType(); + + const withUndefined = await project.checker.getNullableType(stringType, TypeFlags.Undefined); + assert.ok(withUndefined.flags & TypeFlags.Union, `Expected union, got flags ${withUndefined.flags}`); + + const withNull = await project.checker.getNullableType(stringType, TypeFlags.Null); + assert.ok(withNull.flags & TypeFlags.Union, `Expected union, got flags ${withNull.flags}`); + + const withBoth = await project.checker.getNullableType(stringType, TypeFlags.Undefined | TypeFlags.Null); + assert.ok(withBoth.flags & TypeFlags.Union, `Expected union, got flags ${withBoth.flags}`); + + // Adding no flags should just return the original type unchanged. + const withNeither = await project.checker.getNullableType(stringType, 0 as TypeFlags); + assert.equal(withNeither.id, stringType.id); + }); + + test("getFalseType returns the false type", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const falseType = await project.checker.getFalseType(); + assert.ok(falseType.flags & TypeFlags.BooleanLiteral); + assert.equal((falseType as LiteralType).value, false); + }); + + test("getTrueType returns the true type", async () => { + await using api = spawnAPI(intrinsicFiles); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const trueType = await project.checker.getTrueType(); + assert.ok(trueType.flags & TypeFlags.BooleanLiteral); + assert.equal((trueType as LiteralType).value, true); + }); +}); + describe("Checker - multi-project type ID uniqueness", { concurrency }, () => { test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", async () => { await using api = spawnAPI({ @@ -5109,6 +5213,158 @@ export const obj = { name }; }); }); +describe("Checker - getAugmentedPropertiesOfType", { concurrency }, () => { + test("includes CallableFunction members for a function type", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export function foo(x: number): number { return x; }`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const pos = (await project.program.getSourceFile("/src/main.ts"))!.text.indexOf("foo"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + + const props = await project.checker.getAugmentedPropertiesOfType(type); + const names = props.map(p => p.name); + assert.ok(names.includes("call"), `Expected augmented properties to include "call", got: ${names}`); + assert.ok(names.includes("apply"), `Expected augmented properties to include "apply", got: ${names}`); + assert.ok(names.includes("bind"), `Expected augmented properties to include "bind", got: ${names}`); + }); +}); + +describe("Checker - getJsxIntrinsicTagNamesAt", { concurrency }, () => { + test("returns the properties of the JSX.IntrinsicElements interface", async () => { + const src = `export const el =
;\n`; + const jsxTypes = ` +declare namespace JSX { + interface IntrinsicElements { + div: {}; + span: {}; + } + interface Element {} +} +`; + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { jsx: "react" } }), + "/src/jsx.d.ts": jsxTypes, + "/src/main.tsx": src, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.tsx"); + assert.ok(sourceFile); + + let jsxNode: import("@typescript/typescript/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isJsxSelfClosingElement(node)) { + jsxNode = node; + } + node.forEachChild(visit); + }); + assert.ok(jsxNode, "Should find a JSX self-closing element"); + + const tagNames = await project.checker.getJsxIntrinsicTagNamesAt(jsxNode); + const names = tagNames.map(s => s.name); + assert.ok(names.includes("div"), `Expected intrinsic tag names to include "div", got: ${names}`); + assert.ok(names.includes("span"), `Expected intrinsic tag names to include "span", got: ${names}`); + }); +}); + +describe("Checker - getPrivateIdentifierPropertyOfType", { concurrency }, () => { + test("resolves a private field lexically in scope at a location", async () => { + const src = ` +export class Cache { + #bar = 1; + getBar(): number { + return this.#bar; + } +} +`; + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": src, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let privateAccessNode: import("@typescript/typescript/ast").PropertyAccessExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)) { + privateAccessNode = node; + } + node.forEachChild(visit); + }); + assert.ok(privateAccessNode, "Should find `this.#bar`"); + + const thisType = await project.checker.getTypeAtLocation(privateAccessNode.expression); + assert.ok(thisType); + + const name = privateAccessNode.name.text; + assert.equal(name, "#bar"); + + const prop = await project.checker.getPrivateIdentifierPropertyOfType(thisType, name, privateAccessNode.name); + assert.ok(prop, "Should resolve the private field"); + // Private member symbol names are internally encoded (e.g. "__#1@#bar") to disambiguate + // same-named private members across different classes; they still end with the original text. + assert.ok(prop.name.endsWith("#bar"), `Expected symbol name to end with "#bar", got: ${prop.name}`); + }); + + test("returns undefined when the private identifier is not in scope", async () => { + const src = ` +export class Cache { + #bar = 1; +} +export class Other { + #baz = 2; + getBaz(): number { + return this.#baz; + } +} +`; + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": src, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let cacheClass: import("@typescript/typescript/ast").ClassDeclaration | undefined; + let privateAccessNode: import("@typescript/typescript/ast").PropertyAccessExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isClassDeclaration(node) && node.name?.text === "Cache") { + cacheClass = node; + } + if (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)) { + privateAccessNode = node; + } + node.forEachChild(visit); + }); + assert.ok(cacheClass, "Should find class Cache"); + assert.ok(privateAccessNode, "Should find `this.#baz`"); + + const cacheSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", cacheClass.name!.pos + 1); + assert.ok(cacheSymbol); + const cacheType = await project.checker.getDeclaredTypeOfSymbol(cacheSymbol); + assert.ok(cacheType); + + // "#baz" is lexically scoped to class Other, not Cache, so looking it up at Cache's + // location (the class declaration itself) should fail to resolve. + const prop = await project.checker.getPrivateIdentifierPropertyOfType(cacheType, "#baz", cacheClass); + assert.equal(prop, undefined); + }); +}); + describe("readFile callback semantics", { concurrency }, () => { test("callback configurations require every operation at runtime", () => { assert.throws( @@ -6304,6 +6560,46 @@ export type B = InstanceType; }); }); +describe("Checker - isImplementationOfOverload", { concurrency }, () => { + test("returns true only for the implementation signature of overloaded functions", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +function f(x: number): number; +function f(x: string): string; +function f(x: number | string): number | string { + return x; +} +`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const functions = sourceFile.statements.filter(isFunctionDeclaration); + assert.equal(functions.length, 3); + assert.equal(await project.checker.isImplementationOfOverload(functions[0]), false); + assert.equal(await project.checker.isImplementationOfOverload(functions[1]), false); + assert.equal(await project.checker.isImplementationOfOverload(functions[2]), true); + }); + + test("returns false for a non-overloaded function", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `function f(x: number): number { return x; }`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const f = sourceFile.statements.find(isFunctionDeclaration); + assert.ok(f); + assert.equal(await project.checker.isImplementationOfOverload(f), false); + }); +}); + describe("Checker - getReturnTypeOfSignature", { concurrency }, () => { test("returns the return type of a function signature", async () => { await using api = spawnAPI({ @@ -7648,6 +7944,43 @@ export * from "./inner"; }); }); +describe("Checker - getAmbientModules", { concurrency }, () => { + test("returns declared ambient modules", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + // New ambient external modules can only be declared in a script file (not a module) or a `.d.ts` file. + "/src/ambient.d.ts": ` +declare module "foo" { + export const value: number; +} +declare module "bar" { + export const value: string; +} +`, + "/src/main.ts": `export {};`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const ambientModules = await project.checker.getAmbientModules(); + const names = ambientModules.map(m => m.name); + assert.ok(names.includes(`"foo"`), `Should include "foo", found: ${names.join(", ")}`); + assert.ok(names.includes(`"bar"`), `Should include "bar", found: ${names.join(", ")}`); + }); + + test("returns an empty array when there are no ambient modules", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const a = 1;`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const ambientModules = await project.checker.getAmbientModules(); + assert.deepEqual(ambientModules, []); + }); +}); + describe("Checker - getSymbolsInScope", { concurrency }, () => { const scopeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -8145,6 +8478,59 @@ describe("Checker - isContextSensitive", { concurrency }, () => { }); }); +describe("Checker - getPropertySymbolOfDestructuringAssignment", { concurrency }, () => { + test("returns the property symbol for an identifier in a destructuring assignment", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +interface Point { x: number; y: number; } +declare const point: Point; +let x: number; +({ x } = point); +`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + // Find the identifier "x" within the destructuring assignment's left-hand pattern. + let destructuredX: Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isShorthandPropertyAssignment(node) && isIdentifier(node.name) && node.name.text === "x") { + destructuredX = node.name; + } + node.forEachChild(visit); + }); + assert.ok(destructuredX, "Should find the destructured identifier"); + const propertySymbol = await project.checker.getPropertySymbolOfDestructuringAssignment(destructuredX); + assert.ok(propertySymbol); + assert.equal(propertySymbol.name, "x"); + }); + + test("returns undefined for an identifier that is not part of a destructuring assignment", async () => { + await using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `declare const point: { x: number };\npoint;`, + }); + + const snapshot = await api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let pointUsage: Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isExpressionStatement(node) && isIdentifier(node.expression) && node.expression.text === "point") { + pointUsage = node.expression; + } + node.forEachChild(visit); + }); + assert.ok(pointUsage, "Should find the standalone identifier"); + const propertySymbol = await project.checker.getPropertySymbolOfDestructuringAssignment(pointUsage); + assert.equal(propertySymbol, undefined); + }); +}); + describe("Checker - isTypeAssignableTo", { concurrency }, () => { test("returns true when source is assignable to target", async () => { await using api = spawnAPI({ diff --git a/packages/typescript/test/sync/api-generators.test.ts b/packages/typescript/test/sync/api-generators.test.ts index bae80333e1ba3..d8cf33205ec2f 100644 --- a/packages/typescript/test/sync/api-generators.test.ts +++ b/packages/typescript/test/sync/api-generators.test.ts @@ -1728,6 +1728,9 @@ describe("API - generator batching", { concurrency: areTestsFiltered() }, () => parityCase("Checker", "getTypeArguments", checker.getTypeArguments, assertTypeArraysEquivalent, boxedType), parityCase("Checker", "getNonMissingTypeOfSymbol", checker.getNonMissingTypeOfSymbol, assertTypesEquivalent, boxedOptSymbol), parityCase("Checker", "isReadonlySymbol", checker.isReadonlySymbol, assertDeepEquivalent, boxedOptSymbol), + parityCase("Checker", "isImplementationOfOverload", checker.isImplementationOfOverload, assertDeepEquivalent, combineDeclaration), + parityCase("Checker", "getPropertySymbolOfDestructuringAssignment", checker.getPropertySymbolOfDestructuringAssignment, assertOptionalSymbolsEquivalent, importedDerived), + parityCase("Checker", "getAmbientModules", checker.getAmbientModules, assertSymbolArraysEquivalent), parityCase("Checker", "getTargetSymbol", checker.getTargetSymbol, assertOptionalSymbolsEquivalent, boxedOptSymbol), parityCase("Checker", "getExportSymbolOfSymbol", checker.getExportSymbolOfSymbol, assertSymbolsEquivalent, localCombineSymbol), diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index 6d25e0d691258..5c66c31cdc765 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -15,6 +15,7 @@ import { getJSDocTags, getSynthesizedDeepClone, getTextOfJSDocComment, + type Identifier, InternalSymbolName, isCallExpression, isClassDeclaration, @@ -25,9 +26,12 @@ import { isInterfaceDeclaration, isJSDoc, isJSDocParameterTag, + isJsxSelfClosingElement, isModuleDeclaration, isNamedImports, isObjectLiteralExpression, + isPrivateIdentifier, + isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isReturnStatement, @@ -4664,6 +4668,106 @@ describe("Checker - intrinsic type getters", { concurrency }, () => { }); }); +describe("Checker - literal and nullable type getters", { concurrency }, () => { + const intrinsicFiles = { + "/tsconfig.json": "{}", + "/src/main.ts": `export const x = 1;`, + }; + + test("getStringLiteralType returns a StringLiteral type with the given value", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const type = project.checker.getStringLiteralType("hello"); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral); + assert.equal((type as LiteralType).value, "hello"); + + // The checker caches literal types, so asking for the same value twice should + // yield the same underlying type. + const type2 = project.checker.getStringLiteralType("hello"); + assert.equal(type2.id, type.id); + }); + + test("getNumberLiteralType returns a NumberLiteral type with the given value", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const type = project.checker.getNumberLiteralType(42); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + assert.equal((type as NumberLiteralType).value, 42); + + const negType = project.checker.getNumberLiteralType(-1.5); + assert.ok(negType.flags & TypeFlags.NumberLiteral); + assert.equal((negType as NumberLiteralType).value, -1.5); + }); + + test("getBigIntLiteralType returns a BigIntLiteral type with the given value (positive, negative, zero)", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const posType = project.checker.getBigIntLiteralType(123n); + assert.ok(posType.flags & TypeFlags.BigIntLiteral); + assert.equal((posType as BigIntLiteralType).value, 123n); + + const negType = project.checker.getBigIntLiteralType(-123n); + assert.ok(negType.flags & TypeFlags.BigIntLiteral); + assert.equal((negType as BigIntLiteralType).value, -123n); + + const zeroType = project.checker.getBigIntLiteralType(0n); + assert.ok(zeroType.flags & TypeFlags.BigIntLiteral); + assert.equal((zeroType as BigIntLiteralType).value, 0n); + }); + + test("getNullableType adds Undefined and/or Null to a type", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const stringType = project.checker.getStringType(); + + const withUndefined = project.checker.getNullableType(stringType, TypeFlags.Undefined); + assert.ok(withUndefined.flags & TypeFlags.Union, `Expected union, got flags ${withUndefined.flags}`); + + const withNull = project.checker.getNullableType(stringType, TypeFlags.Null); + assert.ok(withNull.flags & TypeFlags.Union, `Expected union, got flags ${withNull.flags}`); + + const withBoth = project.checker.getNullableType(stringType, TypeFlags.Undefined | TypeFlags.Null); + assert.ok(withBoth.flags & TypeFlags.Union, `Expected union, got flags ${withBoth.flags}`); + + // Adding no flags should just return the original type unchanged. + const withNeither = project.checker.getNullableType(stringType, 0 as TypeFlags); + assert.equal(withNeither.id, stringType.id); + }); + + test("getFalseType returns the false type", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const falseType = project.checker.getFalseType(); + assert.ok(falseType.flags & TypeFlags.BooleanLiteral); + assert.equal((falseType as LiteralType).value, false); + }); + + test("getTrueType returns the true type", () => { + using api = spawnAPI(intrinsicFiles); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + + const trueType = project.checker.getTrueType(); + assert.ok(trueType.flags & TypeFlags.BooleanLiteral); + assert.equal((trueType as LiteralType).value, true); + }); +}); + describe("Checker - multi-project type ID uniqueness", { concurrency }, () => { test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", () => { using api = spawnAPI({ @@ -4934,6 +5038,158 @@ export const obj = { name }; }); }); +describe("Checker - getAugmentedPropertiesOfType", { concurrency }, () => { + test("includes CallableFunction members for a function type", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export function foo(x: number): number { return x; }`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const pos = (project.program.getSourceFile("/src/main.ts"))!.text.indexOf("foo"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + + const props = project.checker.getAugmentedPropertiesOfType(type); + const names = props.map(p => p.name); + assert.ok(names.includes("call"), `Expected augmented properties to include "call", got: ${names}`); + assert.ok(names.includes("apply"), `Expected augmented properties to include "apply", got: ${names}`); + assert.ok(names.includes("bind"), `Expected augmented properties to include "bind", got: ${names}`); + }); +}); + +describe("Checker - getJsxIntrinsicTagNamesAt", { concurrency }, () => { + test("returns the properties of the JSX.IntrinsicElements interface", () => { + const src = `export const el =
;\n`; + const jsxTypes = ` +declare namespace JSX { + interface IntrinsicElements { + div: {}; + span: {}; + } + interface Element {} +} +`; + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { jsx: "react" } }), + "/src/jsx.d.ts": jsxTypes, + "/src/main.tsx": src, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.tsx"); + assert.ok(sourceFile); + + let jsxNode: import("@typescript/typescript/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isJsxSelfClosingElement(node)) { + jsxNode = node; + } + node.forEachChild(visit); + }); + assert.ok(jsxNode, "Should find a JSX self-closing element"); + + const tagNames = project.checker.getJsxIntrinsicTagNamesAt(jsxNode); + const names = tagNames.map(s => s.name); + assert.ok(names.includes("div"), `Expected intrinsic tag names to include "div", got: ${names}`); + assert.ok(names.includes("span"), `Expected intrinsic tag names to include "span", got: ${names}`); + }); +}); + +describe("Checker - getPrivateIdentifierPropertyOfType", { concurrency }, () => { + test("resolves a private field lexically in scope at a location", () => { + const src = ` +export class Cache { + #bar = 1; + getBar(): number { + return this.#bar; + } +} +`; + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": src, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let privateAccessNode: import("@typescript/typescript/ast").PropertyAccessExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)) { + privateAccessNode = node; + } + node.forEachChild(visit); + }); + assert.ok(privateAccessNode, "Should find `this.#bar`"); + + const thisType = project.checker.getTypeAtLocation(privateAccessNode.expression); + assert.ok(thisType); + + const name = privateAccessNode.name.text; + assert.equal(name, "#bar"); + + const prop = project.checker.getPrivateIdentifierPropertyOfType(thisType, name, privateAccessNode.name); + assert.ok(prop, "Should resolve the private field"); + // Private member symbol names are internally encoded (e.g. "__#1@#bar") to disambiguate + // same-named private members across different classes; they still end with the original text. + assert.ok(prop.name.endsWith("#bar"), `Expected symbol name to end with "#bar", got: ${prop.name}`); + }); + + test("returns undefined when the private identifier is not in scope", () => { + const src = ` +export class Cache { + #bar = 1; +} +export class Other { + #baz = 2; + getBaz(): number { + return this.#baz; + } +} +`; + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": src, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let cacheClass: import("@typescript/typescript/ast").ClassDeclaration | undefined; + let privateAccessNode: import("@typescript/typescript/ast").PropertyAccessExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isClassDeclaration(node) && node.name?.text === "Cache") { + cacheClass = node; + } + if (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)) { + privateAccessNode = node; + } + node.forEachChild(visit); + }); + assert.ok(cacheClass, "Should find class Cache"); + assert.ok(privateAccessNode, "Should find `this.#baz`"); + + const cacheSymbol = project.checker.getSymbolAtPosition("/src/main.ts", cacheClass.name!.pos + 1); + assert.ok(cacheSymbol); + const cacheType = project.checker.getDeclaredTypeOfSymbol(cacheSymbol); + assert.ok(cacheType); + + // "#baz" is lexically scoped to class Other, not Cache, so looking it up at Cache's + // location (the class declaration itself) should fail to resolve. + const prop = project.checker.getPrivateIdentifierPropertyOfType(cacheType, "#baz", cacheClass); + assert.equal(prop, undefined); + }); +}); + describe("readFile callback semantics", { concurrency }, () => { test("callback configurations require every operation at runtime", () => { assert.throws( @@ -6104,6 +6360,46 @@ export type B = InstanceType; }); }); +describe("Checker - isImplementationOfOverload", { concurrency }, () => { + test("returns true only for the implementation signature of overloaded functions", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +function f(x: number): number; +function f(x: string): string; +function f(x: number | string): number | string { + return x; +} +`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const functions = sourceFile.statements.filter(isFunctionDeclaration); + assert.equal(functions.length, 3); + assert.equal(project.checker.isImplementationOfOverload(functions[0]), false); + assert.equal(project.checker.isImplementationOfOverload(functions[1]), false); + assert.equal(project.checker.isImplementationOfOverload(functions[2]), true); + }); + + test("returns false for a non-overloaded function", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `function f(x: number): number { return x; }`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const f = sourceFile.statements.find(isFunctionDeclaration); + assert.ok(f); + assert.equal(project.checker.isImplementationOfOverload(f), false); + }); +}); + describe("Checker - getReturnTypeOfSignature", { concurrency }, () => { test("returns the return type of a function signature", () => { using api = spawnAPI({ @@ -7448,6 +7744,43 @@ export * from "./inner"; }); }); +describe("Checker - getAmbientModules", { concurrency }, () => { + test("returns declared ambient modules", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + // New ambient external modules can only be declared in a script file (not a module) or a `.d.ts` file. + "/src/ambient.d.ts": ` +declare module "foo" { + export const value: number; +} +declare module "bar" { + export const value: string; +} +`, + "/src/main.ts": `export {};`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const ambientModules = project.checker.getAmbientModules(); + const names = ambientModules.map(m => m.name); + assert.ok(names.includes(`"foo"`), `Should include "foo", found: ${names.join(", ")}`); + assert.ok(names.includes(`"bar"`), `Should include "bar", found: ${names.join(", ")}`); + }); + + test("returns an empty array when there are no ambient modules", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const a = 1;`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const ambientModules = project.checker.getAmbientModules(); + assert.deepEqual(ambientModules, []); + }); +}); + describe("Checker - getSymbolsInScope", { concurrency }, () => { const scopeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -7945,6 +8278,59 @@ describe("Checker - isContextSensitive", { concurrency }, () => { }); }); +describe("Checker - getPropertySymbolOfDestructuringAssignment", { concurrency }, () => { + test("returns the property symbol for an identifier in a destructuring assignment", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +interface Point { x: number; y: number; } +declare const point: Point; +let x: number; +({ x } = point); +`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + // Find the identifier "x" within the destructuring assignment's left-hand pattern. + let destructuredX: Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isShorthandPropertyAssignment(node) && isIdentifier(node.name) && node.name.text === "x") { + destructuredX = node.name; + } + node.forEachChild(visit); + }); + assert.ok(destructuredX, "Should find the destructured identifier"); + const propertySymbol = project.checker.getPropertySymbolOfDestructuringAssignment(destructuredX); + assert.ok(propertySymbol); + assert.equal(propertySymbol.name, "x"); + }); + + test("returns undefined for an identifier that is not part of a destructuring assignment", () => { + using api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `declare const point: { x: number };\npoint;`, + }); + + const snapshot = api.createSnapshot({ openProjects: ["/tsconfig.json"] }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let pointUsage: Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isExpressionStatement(node) && isIdentifier(node.expression) && node.expression.text === "point") { + pointUsage = node.expression; + } + node.forEachChild(visit); + }); + assert.ok(pointUsage, "Should find the standalone identifier"); + const propertySymbol = project.checker.getPropertySymbolOfDestructuringAssignment(pointUsage); + assert.equal(propertySymbol, undefined); + }); +}); + describe("Checker - isTypeAssignableTo", { concurrency }, () => { test("returns true when source is assignable to target", () => { using api = spawnAPI({ diff --git a/tools/scripts/gen/apidiff.mts b/tools/scripts/gen/apidiff.mts new file mode 100644 index 0000000000000..72d93737e1a43 --- /dev/null +++ b/tools/scripts/gen/apidiff.mts @@ -0,0 +1,295 @@ +import fs from "node:fs"; +import path from "node:path"; +import ts from "typescript"; +import { repoRoot } from "./utils.mts"; + +const oldApiPath = path.join(repoRoot, "node_modules", "typescript", "lib", "typescript.d.ts"); +const newApiPackageDirectory = path.join(repoRoot, "packages", "typescript"); +const newApiEntryPoints = [ + "src/api/async/api.ts", + "src/api/fs.ts", + "src/api/proto.ts", + "src/ast/index.ts", + "src/ast/is.ts", + "src/ast/factory.generated.ts", + "src/ast/utils.ts", + "src/ast/scanner.ts", + "src/ast/visitor.ts", + "src/ast/clone.ts", +].map(file => path.join(newApiPackageDirectory, file)); +const outputPath = path.join(repoRoot, "tsc", "testdata", "apidiff.txt"); +const typeMappings = new Map([ + ["TypeChecker", "Checker"], + ["BuilderProgram", "Program"], + ["Program", "Program"], + ["SolutionBuilder", "BuildOrchestrator"], + ["Project", "Project"], + ["LanguageService", "Project"], + ["Scanner", "Scanner"], + ["Printer", "Printer"], + ["Signature", "Signature"], + ["SourceFile", "SourceFile"], + ["Symbol", "Symbol"], + ["Type", "Type"], + ["Node", "Node"], + ["ts", "ts"], +]); + +const typescriptVersion = JSON.parse( + fs.readFileSync(path.join(repoRoot, "node_modules", "typescript", "package.json"), "utf8"), +).version as string; +const oldApiSourceFiles = [createSourceFile(oldApiPath)]; +const newApiProgram = createNewApiProgram(); +const newApiChecker = newApiProgram.getTypeChecker(); +const newApiExports = getEntryPointExports(newApiProgram, newApiChecker, newApiEntryPoints); + +const oldApiTypes = getTypesWithMethods(oldApiSourceFiles, visitOld); +oldApiTypes.set("ts", getNamespaceFunctions(oldApiSourceFiles, "ts")); + +const newApiTypes = getExportedTypesWithMethods(newApiExports); +newApiTypes.set("ts", [getExportedTopLevelFunctions(newApiExports, newApiChecker), new Set()]); + +const acceptedDiffs = getAcceptedDiffs(); + +const report: string[] = [ + "# This report is generated by tools/scripts/gen/apidiff.mts.", + `# Old API: TypeScript ${typescriptVersion} (node_modules/typescript/lib/typescript.d.ts)`, + "# New API: public package entry points", +]; + +const comparisons = [...typeMappings].map(([oldType, newType]) => { + const oldMethods = oldApiTypes.get(oldType)!; + const newMethods = newApiTypes.get(newType)!; + const acceptedChanges = acceptedDiffs.get(oldType) ?? new Map(); + const present: string[] = []; + const missing: string[] = []; + for (const method of oldMethods) { + if (newMethods[0].has(method)) { + present.push(method); + newMethods[0].delete(method); + newMethods[1].add(method); + } + else if (newMethods[1].has(method)) { + present.push(method); + } + else if (acceptedChanges.has(method)) { + present.push(method); + const [newType, newMethodName] = acceptedChanges.get(method)!.split("."); + const actualNewType = newApiTypes.get(newType); + if (actualNewType) { + actualNewType[0].delete(newMethodName); + actualNewType[1].add(method); + } + } + else { + missing.push(method); + } + } + return { + oldType, + newType, + present: present.sort(), + missing: missing.sort(), + }; +}); + +report.push( + `Types not compared:`, +); +for (const [type, _] of oldApiTypes) { + typeMappings.has(type) || report.push(`- ${type}`); +} +report.push( + "", +); + +for (const comparison of comparisons) { + const { oldType, newType, present, missing } = comparison; + report.push( + "", + `# ${oldType} (old) methods compared to ${newType} (new).`, + `## (${present.length}) ${oldType} methods matched in async ${newType} ##`, + "", + `## ${oldType} methods missing from async ${newType} (${missing.length}) ##`, + ...missing, + "", + ); +} + +for (const [newType, newMethods] of newApiTypes) { + report.push( + `## New ${newType} methods in async API (${newMethods[0].size}) ##`, + ...newMethods[0], + "", + ); +} + +fs.writeFileSync(outputPath, report.join("\n")); + +function createSourceFile(file: string): ts.SourceFile { + return ts.createSourceFile( + file, + fs.readFileSync(file, "utf8"), + ts.ScriptTarget.Latest, + true, + ); +} + +function createNewApiProgram(): ts.Program { + const configPath = path.join(newApiPackageDirectory, "tsconfig.json"); + const configFile = ts.readConfigFile(configPath, ts.sys.readFile); + if (configFile.error) { + throw new Error(ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")); + } + const config = ts.parseJsonConfigFileContent(configFile.config, ts.sys, newApiPackageDirectory, { noEmit: true }, configPath); + if (config.errors.length > 0) { + throw new Error(config.errors.map(error => ts.flattenDiagnosticMessageText(error.messageText, "\n")).join("\n")); + } + return ts.createProgram({ + rootNames: config.fileNames, + options: config.options, + projectReferences: config.projectReferences, + }); +} + +type EntryPointExports = ReadonlyMap>; + +function getEntryPointExports(program: ts.Program, checker: ts.TypeChecker, entryPoints: readonly string[]): EntryPointExports { + const exports = new Map>(); + for (const entryPoint of entryPoints) { + const sourceFile = program.getSourceFile(entryPoint); + if (!sourceFile) throw new Error(`Could not find API entry point ${entryPoint}`); + const moduleSymbol = checker.getSymbolAtLocation(sourceFile); + if (!moduleSymbol) throw new Error(`Could not resolve API entry point ${entryPoint}`); + for (const exportedSymbol of checker.getExportsOfModule(moduleSymbol)) { + const symbols = exports.get(exportedSymbol.name) ?? new Set(); + symbols.add(resolveAlias(exportedSymbol, checker)); + exports.set(exportedSymbol.name, symbols); + } + } + return exports; +} + +function resolveAlias(symbol: ts.Symbol, checker: ts.TypeChecker): ts.Symbol { + return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; +} + +function getTypesWithMethods(sourceFiles: readonly ts.SourceFile[], visitNode: (node: ts.Node, types: Map) => void): Map { + const types = new Map(); + for (const sourceFile of sourceFiles) { + visit(sourceFile); + } + return types; + + function visit(node: ts.Node): void { + visitNode(node, types); + ts.forEachChild(node, visit); + } +} + +function getAcceptedDiffs(): Map> { + const diffs = new Map>(); + const contents = fs.readFileSync(path.join(repoRoot, "tsc", "testdata", "apiAccepted.txt"), "utf-8").split("\n"); + contents.slice(2).forEach(line => { + const [oldMethod, newMethod, _] = line.split("|"); + const [oldType, oldMethodName] = oldMethod.split("."); + if (!diffs.has(oldType)) diffs.set(oldType, new Map()); + diffs.get(oldType)!.set(oldMethodName, newMethod); + }); + return diffs; +} + +function visitOld(node: ts.Node, types: Map>): void { + if ((ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.name) { + const methods = types.get(node.name.text) ?? new Set(); + for (const member of node.members) { + if ( + (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) + && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) + && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) + ) { + const name = getPropertyName(member.name); + if (name !== undefined) methods.add(name); + } + } + if (methods.size > 0) types.set(node.name.text, methods); + } +} + +function getExportedTypesWithMethods(exports: EntryPointExports): Map, seen: Set]> { + const types = new Map, seen: Set]>(); + for (const [exportName, symbols] of exports) { + const methods = new Set(); + for (const symbol of symbols) { + for (const declaration of symbol.declarations ?? []) { + if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) { + addPublicMethods(declaration.members, methods); + } + } + } + if (methods.size > 0) types.set(exportName, [methods, new Set()]); + } + return types; +} + +function addPublicMethods(members: ts.NodeArray, methods: Set): void { + for (const member of members) { + if ( + (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) + && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) + && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) + ) { + const name = getPropertyName(member.name); + if (name !== undefined) methods.add(name); + } + } +} + +function getNamespaceFunctions(sourceFiles: readonly ts.SourceFile[], namespace: string): Set { + const functions = new Set(); + for (const sourceFile of sourceFiles) { + for (const statement of sourceFile.statements) { + if ( + ts.isModuleDeclaration(statement) + && statement.name.text === namespace + && statement.body + && ts.isModuleBlock(statement.body) + ) { + for (const member of statement.body.statements) { + if (ts.isFunctionDeclaration(member) && member.name) { + functions.add(member.name.text); + } + } + } + } + } + return functions; +} + +function getExportedTopLevelFunctions(exports: EntryPointExports, checker: ts.TypeChecker): Set { + const functions = new Set(); + for (const [exportName, symbols] of exports) { + for (const symbol of symbols) { + const declaration = symbol.valueDeclaration; + if ( + declaration + && checker.getTypeOfSymbolAtLocation(symbol, declaration).getCallSignatures().length > 0 + ) { + functions.add(exportName); + break; + } + } + } + return functions; +} + +function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean { + return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(modifier => modifier.kind === kind) === true; +} + +function getPropertyName(name: ts.PropertyName): string | undefined { + if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { + return name.text; + } + return undefined; +} diff --git a/tools/scripts/gen/apiold.mts b/tools/scripts/gen/apiold.mts new file mode 100644 index 0000000000000..af6d92f2017c3 --- /dev/null +++ b/tools/scripts/gen/apiold.mts @@ -0,0 +1,104 @@ +import fs from "node:fs"; +import path from "node:path"; +import ts from "typescript"; +import { repoRoot } from "./utils.mts"; + +const oldApiPath = path.join(repoRoot, "node_modules", "typescript", "lib", "typescript.d.ts"); +const outputPath = path.join(repoRoot, "tsc", "testdata", "apiold.txt"); +const typescriptVersion = JSON.parse( + fs.readFileSync(path.join(repoRoot, "node_modules", "typescript", "package.json"), "utf8"), +).version as string; +const types = getTypesWithMethods(oldApiPath); + +const report = [ + "# Types in the old API that declare public methods.", + "# This report is generated by tools/scripts/gen/apiold.mts.", + `# Old API: TypeScript ${typescriptVersion} (node_modules/typescript/lib/typescript.d.ts)`, + "", +]; + +for (const [type, methods] of types) { + report.push(`## ${type} ##`, ...methods, ""); +} + +fs.writeFileSync(outputPath, report.join("\n")); + +function getTypesWithMethods(file: string): Map { + const sourceFile = ts.createSourceFile( + file, + fs.readFileSync(file, "utf8"), + ts.ScriptTarget.Latest, + true, + ); + const types = new Map>(); + visit(sourceFile, []); + + return new Map( + [...types] + .map(([type, methods]) => [type, [...methods].sort()] as const) + .sort(([left], [right]) => left.localeCompare(right)), + ); + + function visit(node: ts.Node, namespace: readonly string[]): void { + const childNamespace = ts.isModuleDeclaration(node) + ? [...namespace, getModuleName(node.name)] + : namespace; + const type = getTypeDeclaration(node); + if (type) { + const methods = getPublicMethods(type.members); + if (methods.size > 0) { + const qualifiedName = [...namespace, type.name.text].join("."); + const existing = types.get(qualifiedName); + if (existing) { + for (const method of methods) existing.add(method); + } + else { + types.set(qualifiedName, methods); + } + } + } + ts.forEachChild(node, child => visit(child, childNamespace)); + } +} + +type NamedTypeDeclaration = (ts.ClassDeclaration | ts.InterfaceDeclaration) & { name: ts.Identifier; }; + +function getTypeDeclaration(node: ts.Node): NamedTypeDeclaration | undefined { + if ( + (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) + && node.name + ) { + return node as NamedTypeDeclaration; + } + return undefined; +} + +function getPublicMethods(members: ts.NodeArray): Set { + const methods = new Set(); + for (const member of members) { + if ( + (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) + && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) + && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) + ) { + const name = getPropertyName(member.name); + if (name !== undefined) methods.add(name); + } + } + return methods; +} + +function getModuleName(name: ts.ModuleName): string { + return name.text; +} + +function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean { + return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(modifier => modifier.kind === kind) === true; +} + +function getPropertyName(name: ts.PropertyName): string | undefined { + if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { + return name.text; + } + return undefined; +} diff --git a/tsc/internal/api/proto.go b/tsc/internal/api/proto.go index 47b04cb15faec..84d034f63b1c3 100644 --- a/tsc/internal/api/proto.go +++ b/tsc/internal/api/proto.go @@ -165,60 +165,72 @@ const ( MethodGetTargetOfSignature Method = "getTargetOfSignature" // Checker methods - MethodGetContextualType Method = "getContextualType" - MethodGetContextualTypeForArgument Method = "getContextualTypeForArgument" - MethodGetAwaitedType Method = "getAwaitedType" - MethodGetBaseTypeOfLiteralType Method = "getBaseTypeOfLiteralType" - MethodGetNonNullableType Method = "getNonNullableType" - MethodGetTypeFromTypeNode Method = "getTypeFromTypeNode" - MethodGetWidenedType Method = "getWidenedType" - MethodGetParameterType Method = "getParameterType" - MethodGetTypeParameterAtPosition Method = "getTypeParameterAtPosition" - MethodIsArrayLikeType Method = "isArrayLikeType" - MethodIsTypeAssignableTo Method = "isTypeAssignableTo" - MethodGetShorthandAssignmentValueSymbol Method = "getShorthandAssignmentValueSymbol" - MethodGetTypeOfSymbolAtLocation Method = "getTypeOfSymbolAtLocation" - MethodTypeToTypeNode Method = "typeToTypeNode" - MethodSignatureToSignatureDeclaration Method = "signatureToSignatureDeclaration" - MethodTypeToString Method = "typeToString" - MethodIsContextSensitive Method = "isContextSensitive" - MethodGetReturnTypeOfSignature Method = "getReturnTypeOfSignature" - MethodGetRestTypeOfSignature Method = "getRestTypeOfSignature" - MethodGetTypePredicateOfSignature Method = "getTypePredicateOfSignature" - MethodGetBaseTypes Method = "getBaseTypes" - MethodGetPropertiesOfType Method = "getPropertiesOfType" - MethodGetApparentPropertiesOfType Method = "getApparentPropertiesOfType" - MethodGetApparentType Method = "getApparentType" - MethodGetReducedType Method = "getReducedType" - MethodGetPropertyOfType Method = "getPropertyOfType" - MethodGetTypeOfPropertyOfType Method = "getTypeOfPropertyOfType" - MethodGetIndexInfoOfType Method = "getIndexInfoOfType" - MethodGetIndexInfosOfType Method = "getIndexInfosOfType" - MethodGetConstraintOfTypeParameter Method = "getConstraintOfTypeParameter" - MethodGetDefaultFromTypeParameter Method = "getDefaultFromTypeParameter" - MethodGetBaseConstraintOfType Method = "getBaseConstraintOfType" - MethodGetTypeArguments Method = "getTypeArguments" - MethodGetImportAdderEdits Method = "getImportAdderEdits" - MethodGetTrueTypeOfConditionalType Method = "getTrueTypeOfConditionalType" - MethodGetFalseTypeOfConditionalType Method = "getFalseTypeOfConditionalType" - MethodGetConstantValue Method = "getConstantValue" - MethodGetSignatureFromDeclaration Method = "getSignatureFromDeclaration" - MethodGetExportSpecifierLocalTarget Method = "getExportSpecifierLocalTargetSymbol" - MethodGetAliasedSymbol Method = "getAliasedSymbol" - MethodGetImmediateAliasedSymbol Method = "getImmediateAliasedSymbol" - MethodGetTargetSymbol Method = "getTargetSymbol" - MethodGetMergedSymbol Method = "getMergedSymbol" - MethodGetSymbolOfNode Method = "getSymbolOfNode" - MethodGetSymbolOfDeclarationForChecker Method = "getSymbolOfDeclarationForChecker" - MethodGetParentOfSymbolForChecker Method = "getParentOfSymbolForChecker" - MethodGetExportSymbolOfSymbolForChecker Method = "getExportSymbolOfSymbolForChecker" - MethodGetFullyQualifiedName Method = "getFullyQualifiedName" - MethodGetExportsOfModule Method = "getExportsOfModule" - MethodGetMemberInModuleExports Method = "getMemberInModuleExports" - MethodGetJSDocTags Method = "getJsDocTags" - MethodGetDocumentationComment Method = "getDocumentationComment" - MethodIsArrayType Method = "isArrayType" - MethodIsReadonlySymbol Method = "isReadonlySymbol" + MethodGetContextualType Method = "getContextualType" + MethodGetContextualTypeForArgument Method = "getContextualTypeForArgument" + MethodGetAwaitedType Method = "getAwaitedType" + MethodGetBaseTypeOfLiteralType Method = "getBaseTypeOfLiteralType" + MethodGetNonNullableType Method = "getNonNullableType" + MethodGetNullableType Method = "getNullableType" + MethodGetStringLiteralType Method = "getStringLiteralType" + MethodGetNumberLiteralType Method = "getNumberLiteralType" + MethodGetBigIntLiteralType Method = "getBigIntLiteralType" + MethodGetFalseType Method = "getFalseType" + MethodGetTrueType Method = "getTrueType" + MethodGetAugmentedPropertiesOfType Method = "getAugmentedPropertiesOfType" + MethodGetJsxIntrinsicTagNamesAt Method = "getJsxIntrinsicTagNamesAt" + MethodGetPrivateIdentifierPropertyOfType Method = "getPrivateIdentifierPropertyOfType" + MethodGetTypeFromTypeNode Method = "getTypeFromTypeNode" + MethodGetWidenedType Method = "getWidenedType" + MethodGetParameterType Method = "getParameterType" + MethodGetTypeParameterAtPosition Method = "getTypeParameterAtPosition" + MethodIsArrayLikeType Method = "isArrayLikeType" + MethodIsTypeAssignableTo Method = "isTypeAssignableTo" + MethodGetShorthandAssignmentValueSymbol Method = "getShorthandAssignmentValueSymbol" + MethodGetTypeOfSymbolAtLocation Method = "getTypeOfSymbolAtLocation" + MethodTypeToTypeNode Method = "typeToTypeNode" + MethodSignatureToSignatureDeclaration Method = "signatureToSignatureDeclaration" + MethodTypeToString Method = "typeToString" + MethodIsContextSensitive Method = "isContextSensitive" + MethodGetReturnTypeOfSignature Method = "getReturnTypeOfSignature" + MethodGetRestTypeOfSignature Method = "getRestTypeOfSignature" + MethodGetTypePredicateOfSignature Method = "getTypePredicateOfSignature" + MethodGetBaseTypes Method = "getBaseTypes" + MethodGetPropertiesOfType Method = "getPropertiesOfType" + MethodGetApparentPropertiesOfType Method = "getApparentPropertiesOfType" + MethodGetApparentType Method = "getApparentType" + MethodGetReducedType Method = "getReducedType" + MethodGetPropertyOfType Method = "getPropertyOfType" + MethodGetTypeOfPropertyOfType Method = "getTypeOfPropertyOfType" + MethodGetIndexInfoOfType Method = "getIndexInfoOfType" + MethodGetIndexInfosOfType Method = "getIndexInfosOfType" + MethodGetConstraintOfTypeParameter Method = "getConstraintOfTypeParameter" + MethodGetDefaultFromTypeParameter Method = "getDefaultFromTypeParameter" + MethodGetBaseConstraintOfType Method = "getBaseConstraintOfType" + MethodGetTypeArguments Method = "getTypeArguments" + MethodGetImportAdderEdits Method = "getImportAdderEdits" + MethodGetTrueTypeOfConditionalType Method = "getTrueTypeOfConditionalType" + MethodGetFalseTypeOfConditionalType Method = "getFalseTypeOfConditionalType" + MethodGetConstantValue Method = "getConstantValue" + MethodGetSignatureFromDeclaration Method = "getSignatureFromDeclaration" + MethodGetExportSpecifierLocalTarget Method = "getExportSpecifierLocalTargetSymbol" + MethodGetAliasedSymbol Method = "getAliasedSymbol" + MethodGetImmediateAliasedSymbol Method = "getImmediateAliasedSymbol" + MethodGetTargetSymbol Method = "getTargetSymbol" + MethodGetMergedSymbol Method = "getMergedSymbol" + MethodGetSymbolOfNode Method = "getSymbolOfNode" + MethodGetSymbolOfDeclarationForChecker Method = "getSymbolOfDeclarationForChecker" + MethodGetParentOfSymbolForChecker Method = "getParentOfSymbolForChecker" + MethodGetExportSymbolOfSymbolForChecker Method = "getExportSymbolOfSymbolForChecker" + MethodGetFullyQualifiedName Method = "getFullyQualifiedName" + MethodGetExportsOfModule Method = "getExportsOfModule" + MethodGetMemberInModuleExports Method = "getMemberInModuleExports" + MethodGetJSDocTags Method = "getJsDocTags" + MethodGetDocumentationComment Method = "getDocumentationComment" + MethodIsArrayType Method = "isArrayType" + MethodIsReadonlySymbol Method = "isReadonlySymbol" + MethodIsImplementationOfOverload Method = "isImplementationOfOverload" + MethodGetPropertySymbolOfDestructuringAssignment Method = "getPropertySymbolOfDestructuringAssignment" + MethodGetAmbientModules Method = "getAmbientModules" // Reference methods MethodGetReferencesToSymbolInFile Method = "getReferencesToSymbolInFile" @@ -690,93 +702,105 @@ var unmarshalers = map[Method]func([]byte) (any, error){ MethodGetThisParameterOfSignature: unmarshallerFor[GetSignaturePropertyParams], MethodGetTargetOfSignature: unmarshallerFor[GetSignaturePropertyParams], - MethodGetContextualType: unmarshallerFor[GetContextualTypeParams], - MethodGetContextualTypeForArgument: unmarshallerFor[GetContextualTypeForArgumentParams], - MethodGetAwaitedType: unmarshallerFor[CheckerTypeParams], - MethodGetBaseTypeOfLiteralType: unmarshallerFor[GetBaseTypeOfLiteralTypeParams], - MethodGetNonNullableType: unmarshallerFor[GetTypePropertyParams], - MethodGetTypeFromTypeNode: unmarshallerFor[GetTypeFromTypeNodeParams], - MethodGetWidenedType: unmarshallerFor[GetWidenedTypeParams], - MethodGetParameterType: unmarshallerFor[GetParameterTypeParams], - MethodGetTypeParameterAtPosition: unmarshallerFor[GetParameterTypeParams], - MethodIsArrayLikeType: unmarshallerFor[IsArrayLikeTypeParams], - MethodIsTypeAssignableTo: unmarshallerFor[IsTypeAssignableToParams], - MethodGetShorthandAssignmentValueSymbol: unmarshallerFor[GetTypeAtLocationParams], - MethodGetTypeOfSymbolAtLocation: unmarshallerFor[GetTypeOfSymbolAtLocationParams], - MethodTypeToTypeNode: unmarshallerFor[TypeToTypeNodeParams], - MethodSignatureToSignatureDeclaration: unmarshallerFor[SignatureToSignatureDeclarationParams], - MethodTypeToString: unmarshallerFor[TypeToTypeNodeParams], - MethodIsContextSensitive: unmarshallerFor[GetContextualTypeParams], - MethodGetReturnTypeOfSignature: unmarshallerFor[GetSignaturePropertyParams], - MethodGetRestTypeOfSignature: unmarshallerFor[CheckerSignatureParams], - MethodGetTypePredicateOfSignature: unmarshallerFor[CheckerSignatureParams], - MethodGetBaseTypes: unmarshallerFor[CheckerTypeParams], - MethodGetPropertiesOfType: unmarshallerFor[CheckerTypeParams], - MethodGetApparentPropertiesOfType: unmarshallerFor[GetTypePropertyParams], - MethodGetApparentType: unmarshallerFor[GetTypePropertyParams], - MethodGetReducedType: unmarshallerFor[GetTypePropertyParams], - MethodGetPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams], - MethodGetTypeOfPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams], - MethodGetIndexInfoOfType: unmarshallerFor[GetIndexInfoOfTypeParams], - MethodGetIndexInfosOfType: unmarshallerFor[CheckerTypeParams], - MethodGetConstraintOfTypeParameter: unmarshallerFor[GetTypePropertyParams], - MethodGetBaseConstraintOfType: unmarshallerFor[CheckerTypeParams], - MethodGetDefaultFromTypeParameter: unmarshallerFor[GetTypePropertyParams], - MethodGetTypeArguments: unmarshallerFor[CheckerTypeParams], - MethodGetImportAdderEdits: unmarshallerFor[GetImportAdderEditsParams], - MethodGetConstantValue: unmarshallerFor[CheckerNodeParams], - MethodGetSignatureFromDeclaration: unmarshallerFor[CheckerNodeParams], - MethodGetExportSpecifierLocalTarget: unmarshallerFor[CheckerNodeParams], - MethodGetAliasedSymbol: unmarshallerFor[CheckerSymbolParams], - MethodGetImmediateAliasedSymbol: unmarshallerFor[CheckerSymbolParams], - MethodGetTargetSymbol: unmarshallerFor[CheckerSymbolParams], - MethodGetMergedSymbol: unmarshallerFor[CheckerSymbolParams], - MethodGetSymbolOfNode: unmarshallerFor[CheckerNodeParams], - MethodGetSymbolOfDeclarationForChecker: unmarshallerFor[CheckerNodeParams], - MethodGetParentOfSymbolForChecker: unmarshallerFor[CheckerSymbolParams], - MethodGetExportSymbolOfSymbolForChecker: unmarshallerFor[CheckerSymbolParams], - MethodGetFullyQualifiedName: unmarshallerFor[CheckerSymbolParams], - MethodGetExportsOfModule: unmarshallerFor[CheckerSymbolParams], - MethodGetMemberInModuleExports: unmarshallerFor[GetMemberInModuleExportsParams], - MethodGetJSDocTags: unmarshallerFor[CheckerSymbolParams], - MethodGetDocumentationComment: unmarshallerFor[CheckerSymbolParams], - MethodIsArrayType: unmarshallerFor[CheckerTypeParams], - MethodIsReadonlySymbol: unmarshallerFor[CheckerSymbolParams], - MethodGetReferencesToSymbolInFile: unmarshallerFor[GetReferencesToSymbolInFileParams], - MethodGetReferencedSymbolsForNode: unmarshallerFor[GetReferencedSymbolsForNodeParams], - MethodGetSignatureUsages: unmarshallerFor[GetSignatureUsagesParams], - MethodGetCompletionsAtPosition: unmarshallerFor[GetCompletionsAtPositionParams], - MethodFormatNodeForInsertion: unmarshallerFor[FormatNodeForInsertionParams], - MethodPrintNode: unmarshallerFor[PrintNodeParams], - MethodEmit: unmarshallerFor[EmitParams], - MethodEmitToString: unmarshallerFor[EmitParams], - MethodGetJavaScriptEmit: unmarshallerFor[SelectedFilesEmitParams], - MethodGetDeclarationEmit: unmarshallerFor[SelectedFilesEmitParams], - MethodGetAnyType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetStringType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetNumberType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetBooleanType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetVoidType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetUndefinedType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetNullType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetNeverType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetUnknownType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetBigIntType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetESSymbolType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetNonPrimitiveType: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetWellKnownSymbols: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetWellKnownSignatures: unmarshallerFor[GetIntrinsicTypeParams], - MethodGetSyntacticDiagnostics: unmarshallerFor[GetDiagnosticsParams], - MethodGetBindDiagnostics: unmarshallerFor[GetDiagnosticsParams], - MethodGetSemanticDiagnostics: unmarshallerFor[GetDiagnosticsParams], - MethodGetSuggestionDiagnostics: unmarshallerFor[GetDiagnosticsParams], - MethodGetDeclarationDiagnostics: unmarshallerFor[GetDiagnosticsParams], - MethodGetProgramDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], - MethodGetGlobalDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], - MethodGetConfigFileParsingDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], - MethodStartCPUProfile: unmarshallerFor[ProfileParams], - MethodStopCPUProfile: noParams, - MethodSaveHeapProfile: unmarshallerFor[ProfileParams], + MethodGetContextualType: unmarshallerFor[GetContextualTypeParams], + MethodGetContextualTypeForArgument: unmarshallerFor[GetContextualTypeForArgumentParams], + MethodGetAwaitedType: unmarshallerFor[CheckerTypeParams], + MethodGetBaseTypeOfLiteralType: unmarshallerFor[GetBaseTypeOfLiteralTypeParams], + MethodGetNonNullableType: unmarshallerFor[GetTypePropertyParams], + MethodGetNullableType: unmarshallerFor[GetNullableTypeParams], + MethodGetStringLiteralType: unmarshallerFor[GetStringLiteralTypeParams], + MethodGetNumberLiteralType: unmarshallerFor[GetNumberLiteralTypeParams], + MethodGetBigIntLiteralType: unmarshallerFor[GetBigIntLiteralTypeParams], + MethodGetFalseType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetTrueType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetAugmentedPropertiesOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetJsxIntrinsicTagNamesAt: unmarshallerFor[CheckerNodeParams], + MethodGetPrivateIdentifierPropertyOfType: unmarshallerFor[GetPrivateIdentifierPropertyOfTypeParams], + MethodGetTypeFromTypeNode: unmarshallerFor[GetTypeFromTypeNodeParams], + MethodGetWidenedType: unmarshallerFor[GetWidenedTypeParams], + MethodGetParameterType: unmarshallerFor[GetParameterTypeParams], + MethodGetTypeParameterAtPosition: unmarshallerFor[GetParameterTypeParams], + MethodIsArrayLikeType: unmarshallerFor[IsArrayLikeTypeParams], + MethodIsTypeAssignableTo: unmarshallerFor[IsTypeAssignableToParams], + MethodGetShorthandAssignmentValueSymbol: unmarshallerFor[GetTypeAtLocationParams], + MethodGetTypeOfSymbolAtLocation: unmarshallerFor[GetTypeOfSymbolAtLocationParams], + MethodTypeToTypeNode: unmarshallerFor[TypeToTypeNodeParams], + MethodSignatureToSignatureDeclaration: unmarshallerFor[SignatureToSignatureDeclarationParams], + MethodTypeToString: unmarshallerFor[TypeToTypeNodeParams], + MethodIsContextSensitive: unmarshallerFor[GetContextualTypeParams], + MethodGetReturnTypeOfSignature: unmarshallerFor[GetSignaturePropertyParams], + MethodGetRestTypeOfSignature: unmarshallerFor[CheckerSignatureParams], + MethodGetTypePredicateOfSignature: unmarshallerFor[CheckerSignatureParams], + MethodGetBaseTypes: unmarshallerFor[CheckerTypeParams], + MethodGetPropertiesOfType: unmarshallerFor[CheckerTypeParams], + MethodGetApparentPropertiesOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetApparentType: unmarshallerFor[GetTypePropertyParams], + MethodGetReducedType: unmarshallerFor[GetTypePropertyParams], + MethodGetPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams], + MethodGetTypeOfPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams], + MethodGetIndexInfoOfType: unmarshallerFor[GetIndexInfoOfTypeParams], + MethodGetIndexInfosOfType: unmarshallerFor[CheckerTypeParams], + MethodGetConstraintOfTypeParameter: unmarshallerFor[GetTypePropertyParams], + MethodGetBaseConstraintOfType: unmarshallerFor[CheckerTypeParams], + MethodGetDefaultFromTypeParameter: unmarshallerFor[GetTypePropertyParams], + MethodGetTypeArguments: unmarshallerFor[CheckerTypeParams], + MethodGetImportAdderEdits: unmarshallerFor[GetImportAdderEditsParams], + MethodGetConstantValue: unmarshallerFor[CheckerNodeParams], + MethodGetSignatureFromDeclaration: unmarshallerFor[CheckerNodeParams], + MethodGetExportSpecifierLocalTarget: unmarshallerFor[CheckerNodeParams], + MethodGetAliasedSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetImmediateAliasedSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetTargetSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetMergedSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetSymbolOfNode: unmarshallerFor[CheckerNodeParams], + MethodGetSymbolOfDeclarationForChecker: unmarshallerFor[CheckerNodeParams], + MethodGetParentOfSymbolForChecker: unmarshallerFor[CheckerSymbolParams], + MethodGetExportSymbolOfSymbolForChecker: unmarshallerFor[CheckerSymbolParams], + MethodGetFullyQualifiedName: unmarshallerFor[CheckerSymbolParams], + MethodGetExportsOfModule: unmarshallerFor[CheckerSymbolParams], + MethodGetMemberInModuleExports: unmarshallerFor[GetMemberInModuleExportsParams], + MethodGetJSDocTags: unmarshallerFor[CheckerSymbolParams], + MethodGetDocumentationComment: unmarshallerFor[CheckerSymbolParams], + MethodIsArrayType: unmarshallerFor[CheckerTypeParams], + MethodIsReadonlySymbol: unmarshallerFor[CheckerSymbolParams], + MethodIsImplementationOfOverload: unmarshallerFor[CheckerNodeParams], + MethodGetPropertySymbolOfDestructuringAssignment: unmarshallerFor[CheckerNodeParams], + MethodGetAmbientModules: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetReferencesToSymbolInFile: unmarshallerFor[GetReferencesToSymbolInFileParams], + MethodGetReferencedSymbolsForNode: unmarshallerFor[GetReferencedSymbolsForNodeParams], + MethodGetSignatureUsages: unmarshallerFor[GetSignatureUsagesParams], + MethodGetCompletionsAtPosition: unmarshallerFor[GetCompletionsAtPositionParams], + MethodPrintNode: unmarshallerFor[PrintNodeParams], + MethodFormatNodeForInsertion: unmarshallerFor[FormatNodeForInsertionParams], + MethodEmit: unmarshallerFor[EmitParams], + MethodEmitToString: unmarshallerFor[EmitParams], + MethodGetJavaScriptEmit: unmarshallerFor[SelectedFilesEmitParams], + MethodGetDeclarationEmit: unmarshallerFor[SelectedFilesEmitParams], + MethodGetAnyType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetStringType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNumberType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetBooleanType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetVoidType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetUndefinedType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNullType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNeverType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetUnknownType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetBigIntType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetESSymbolType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNonPrimitiveType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetWellKnownSymbols: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetWellKnownSignatures: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetSyntacticDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetBindDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetSemanticDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetSuggestionDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetDeclarationDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetProgramDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodGetGlobalDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodGetConfigFileParsingDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodStartCPUProfile: unmarshallerFor[ProfileParams], + MethodStopCPUProfile: noParams, + MethodSaveHeapProfile: unmarshallerFor[ProfileParams], } type ParseConfigFileParams struct { @@ -1659,6 +1683,46 @@ type GetNonNullableTypeParams struct { Type TypeID `json:"type"` } +// GetNullableTypeParams are the parameters for the getNullableType method. +type GetNullableTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project project.Handle `json:"project"` + Type TypeID `json:"type"` + Flags int32 `json:"flags"` +} + +// GetStringLiteralTypeParams are the parameters for the getStringLiteralType method. +type GetStringLiteralTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project project.Handle `json:"project"` + Value string `json:"value"` +} + +// GetNumberLiteralTypeParams are the parameters for the getNumberLiteralType method. +type GetNumberLiteralTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project project.Handle `json:"project"` + Value float64 `json:"value"` +} + +// GetBigIntLiteralTypeParams are the parameters for the getBigIntLiteralType method. +// Base10Value is the absolute value in base 10 with no leading zeros (empty for zero). +type GetBigIntLiteralTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project project.Handle `json:"project"` + Negative bool `json:"negative,omitempty"` + Base10Value string `json:"base10Value,omitempty"` +} + +// GetPrivateIdentifierPropertyOfTypeParams are the parameters for the getPrivateIdentifierPropertyOfType method. +type GetPrivateIdentifierPropertyOfTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project project.Handle `json:"project"` + Type TypeID `json:"type"` + Name string `json:"name"` + Location NodeHandle `json:"location"` +} + // GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. type GetTypeFromTypeNodeParams struct { Snapshot SnapshotID `json:"snapshot"` diff --git a/tsc/internal/api/session.go b/tsc/internal/api/session.go index a434123b2b54d..cb6799c551c77 100644 --- a/tsc/internal/api/session.go +++ b/tsc/internal/api/session.go @@ -1077,6 +1077,20 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json. return s.handleGetBaseTypeOfLiteralType(ctx, parsed.(*GetBaseTypeOfLiteralTypeParams)) case string(MethodGetNonNullableType): return s.handleGetNonNullableType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetNullableType): + return s.handleGetNullableType(ctx, parsed.(*GetNullableTypeParams)) + case string(MethodGetStringLiteralType): + return s.handleGetStringLiteralType(ctx, parsed.(*GetStringLiteralTypeParams)) + case string(MethodGetNumberLiteralType): + return s.handleGetNumberLiteralType(ctx, parsed.(*GetNumberLiteralTypeParams)) + case string(MethodGetBigIntLiteralType): + return s.handleGetBigIntLiteralType(ctx, parsed.(*GetBigIntLiteralTypeParams)) + case string(MethodGetAugmentedPropertiesOfType): + return s.handleGetAugmentedPropertiesOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetJsxIntrinsicTagNamesAt): + return s.handleGetJsxIntrinsicTagNamesAt(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetPrivateIdentifierPropertyOfType): + return s.handleGetPrivateIdentifierPropertyOfType(ctx, parsed.(*GetPrivateIdentifierPropertyOfTypeParams)) case string(MethodGetTypeFromTypeNode): return s.handleGetTypeFromTypeNode(ctx, parsed.(*GetTypeFromTypeNodeParams)) case string(MethodGetWidenedType): @@ -1181,6 +1195,12 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json. return s.handleIsArrayType(ctx, parsed.(*CheckerTypeParams)) case string(MethodIsReadonlySymbol): return s.handleIsReadonlySymbol(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodIsImplementationOfOverload): + return s.handleIsImplementationOfOverload(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetPropertySymbolOfDestructuringAssignment): + return s.handleGetPropertySymbolOfDestructuringAssignment(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetAmbientModules): + return s.handleGetAmbientModules(ctx, parsed.(*GetIntrinsicTypeParams)) case string(MethodGetAnyType): return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetAnyType) case string(MethodGetStringType): @@ -1205,6 +1225,10 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json. return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetESSymbolType) case string(MethodGetNonPrimitiveType): return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetNonPrimitiveType) + case string(MethodGetFalseType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetFalseType) + case string(MethodGetTrueType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetTrueType) case string(MethodGetWellKnownSymbols): return s.handleGetWellKnownSymbols(ctx, parsed.(*GetIntrinsicTypeParams)) case string(MethodGetWellKnownSignatures): @@ -3714,6 +3738,147 @@ func (s *Session) handleGetNonNullableType(ctx context.Context, params *GetTypeP return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetNonNullableType(t), setup.checker), nil } +// handleGetNullableType adds undefined and/or null to a type if they are missing, as indicated by flags. +func (s *Session) handleGetNullableType(ctx context.Context, params *GetNullableTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + result := setup.checker.GetNullableType(t, checker.TypeFlags(params.Flags)) + return setup.sd.newTypeResponse(setup.projectID, result, setup.checker), nil +} + +// handleGetStringLiteralType returns (or creates) the singleton type for a string literal value. +func (s *Session) handleGetStringLiteralType(ctx context.Context, params *GetStringLiteralTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t := setup.checker.GetStringLiteralType(params.Value) + return setup.sd.newTypeResponse(setup.projectID, t, setup.checker), nil +} + +// handleGetNumberLiteralType returns (or creates) the singleton type for a numeric literal value. +func (s *Session) handleGetNumberLiteralType(ctx context.Context, params *GetNumberLiteralTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t := setup.checker.GetNumberLiteralType(jsnum.Number(params.Value)) + return setup.sd.newTypeResponse(setup.projectID, t, setup.checker), nil +} + +// handleGetBigIntLiteralType returns (or creates) the singleton type for a bigint literal value. +func (s *Session) handleGetBigIntLiteralType(ctx context.Context, params *GetBigIntLiteralTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + value := jsnum.PseudoBigInt{Negative: params.Negative, Base10Value: params.Base10Value} + t := setup.checker.GetBigIntLiteralType(value) + return setup.sd.newTypeResponse(setup.projectID, t, setup.checker), nil +} + +// handleGetAugmentedPropertiesOfType returns the augmented properties of a type: its properties, +// plus (when the type has call or construct signatures) the properties of the global +// CallableFunction/NewableFunction interfaces not already present. +// @gen-proto-nullable +func (s *Session) handleGetAugmentedPropertiesOfType(ctx context.Context, params *GetTypePropertyParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + props := setup.checker.GetApparentProperties(t) + if len(props) == 0 { + return nil, nil + } + + results := make([]*SymbolResponse, len(props)) + for i, prop := range props { + results[i] = setup.newSymbolResponse(prop) + } + return results, nil +} + +// handleGetJsxIntrinsicTagNamesAt returns all the properties of the Jsx.IntrinsicElements interface. +// @gen-proto-nullable +func (s *Session) handleGetJsxIntrinsicTagNamesAt(ctx context.Context, params *CheckerNodeParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + props := setup.checker.GetJsxIntrinsicTagNamesAt(node) + if len(props) == 0 { + return nil, nil + } + + results := make([]*SymbolResponse, len(props)) + for i, prop := range props { + results[i] = setup.newSymbolResponse(prop) + } + return results, nil +} + +// handleGetPrivateIdentifierPropertyOfType looks up a private identifier (e.g. "#foo") lexically in +// scope at the given location, and if found, returns the corresponding property of the type. +func (s *Session) handleGetPrivateIdentifierPropertyOfType(ctx context.Context, params *GetPrivateIdentifierPropertyOfTypeParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetPrivateIdentifierPropertyOfType(t, params.Name, node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + // handleGetTypeFromTypeNode returns the type for a type node. func (s *Session) handleGetTypeFromTypeNode(ctx context.Context, params *GetTypeFromTypeNodeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) @@ -4386,6 +4551,76 @@ func (s *Session) handleIsReadonlySymbol(ctx context.Context, params *CheckerSym return setup.checker.IsReadonlySymbol(symbol), nil } +// handleIsImplementationOfOverload returns whether a function-like declaration +// is the implementation signature of a set of overloads. +func (s *Session) handleIsImplementationOfOverload(ctx context.Context, params *CheckerNodeParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return false, err + } + if node == nil { + return false, nil + } + + return setup.checker.NewEmitResolver(printer.NewEmitContext()).IsImplementationOfOverload(node), nil +} + +// handleGetPropertySymbolOfDestructuringAssignment returns the property symbol +// corresponding to an identifier in a destructuring assignment or for-of statement. +// @gen-proto-nullable +func (s *Session) handleGetPropertySymbolOfDestructuringAssignment(ctx context.Context, params *CheckerNodeParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetPropertySymbolOfDestructuringAssignment(node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetAmbientModules returns all ambient modules declared in the program, +// such as `declare module "foo" { ... }`. +// @gen-proto-nullable +func (s *Session) handleGetAmbientModules(ctx context.Context, params *GetIntrinsicTypeParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + modules := setup.checker.GetAmbientModules() + if len(modules) == 0 { + return nil, nil + } + slices.SortFunc(modules, setup.checker.CompareSymbols) + + results := make([]*SymbolResponse, len(modules)) + for i, module := range modules { + results[i] = setup.newSymbolResponse(module) + } + + return results, nil +} + // handleGetBaseTypes returns the base types of an interface/class type. // @gen-proto-nullable func (s *Session) handleGetBaseTypes(ctx context.Context, params *CheckerTypeParams) ([]*TypeResponse, error) { diff --git a/tsc/internal/checker/exports.go b/tsc/internal/checker/exports.go index f03be066141dc..bf54fcaaa171f 100644 --- a/tsc/internal/checker/exports.go +++ b/tsc/internal/checker/exports.go @@ -5,6 +5,7 @@ import ( "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/diagnostics" + "github.com/microsoft/TypeScript/tsc/internal/jsnum" ) func (c *Checker) GetStringType() *Type { @@ -59,10 +60,45 @@ func (c *Checker) GetNonPrimitiveType() *Type { return c.nonPrimitiveType } +func (c *Checker) GetStringLiteralType(value string) *Type { + return c.getStringLiteralType(value) +} + +func (c *Checker) GetNumberLiteralType(value jsnum.Number) *Type { + return c.getNumberLiteralType(value) +} + +func (c *Checker) GetBigIntLiteralType(value jsnum.PseudoBigInt) *Type { + return c.getBigIntLiteralType(value) +} + +func (c *Checker) GetNullableType(t *Type, flags TypeFlags) *Type { + return c.getNullableType(t, flags) +} + +func (c *Checker) GetFalseType() *Type { + return c.regularFalseType +} + +func (c *Checker) GetTrueType() *Type { + return c.regularTrueType +} + func (c *Checker) GetBaseTypeOfLiteralType(t *Type) *Type { return c.getBaseTypeOfLiteralType(t) } +// GetPrivateIdentifierPropertyOfType looks up a private identifier named name (e.g. "#foo", including +// the leading '#') lexically in scope at location, and if it resolves to a symbol, returns the +// corresponding property of leftType (or nil if leftType has no such property). +func (c *Checker) GetPrivateIdentifierPropertyOfType(leftType *Type, name string, location *ast.Node) *ast.Symbol { + lexicallyScopedIdentifier := c.lookupSymbolForPrivateIdentifierDeclaration(name, location) + if lexicallyScopedIdentifier == nil { + return nil + } + return c.getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedIdentifier) +} + func (c *Checker) GetUnknownSymbol() *ast.Symbol { return c.unknownSymbol } From 6129608dde16b89da10fc9f7228cd5006a8bcd2f Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 29 Oct 2026 14:21:03 -0700 Subject: [PATCH 2/3] clean up --- tools/scripts/gen/apidiff.mts | 295 ---------------------------------- tools/scripts/gen/apiold.mts | 104 ------------ tsc/internal/api/proto.go | 10 -- tsc/internal/api/session.go | 19 +-- 4 files changed, 2 insertions(+), 426 deletions(-) delete mode 100644 tools/scripts/gen/apidiff.mts delete mode 100644 tools/scripts/gen/apiold.mts diff --git a/tools/scripts/gen/apidiff.mts b/tools/scripts/gen/apidiff.mts deleted file mode 100644 index 72d93737e1a43..0000000000000 --- a/tools/scripts/gen/apidiff.mts +++ /dev/null @@ -1,295 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import ts from "typescript"; -import { repoRoot } from "./utils.mts"; - -const oldApiPath = path.join(repoRoot, "node_modules", "typescript", "lib", "typescript.d.ts"); -const newApiPackageDirectory = path.join(repoRoot, "packages", "typescript"); -const newApiEntryPoints = [ - "src/api/async/api.ts", - "src/api/fs.ts", - "src/api/proto.ts", - "src/ast/index.ts", - "src/ast/is.ts", - "src/ast/factory.generated.ts", - "src/ast/utils.ts", - "src/ast/scanner.ts", - "src/ast/visitor.ts", - "src/ast/clone.ts", -].map(file => path.join(newApiPackageDirectory, file)); -const outputPath = path.join(repoRoot, "tsc", "testdata", "apidiff.txt"); -const typeMappings = new Map([ - ["TypeChecker", "Checker"], - ["BuilderProgram", "Program"], - ["Program", "Program"], - ["SolutionBuilder", "BuildOrchestrator"], - ["Project", "Project"], - ["LanguageService", "Project"], - ["Scanner", "Scanner"], - ["Printer", "Printer"], - ["Signature", "Signature"], - ["SourceFile", "SourceFile"], - ["Symbol", "Symbol"], - ["Type", "Type"], - ["Node", "Node"], - ["ts", "ts"], -]); - -const typescriptVersion = JSON.parse( - fs.readFileSync(path.join(repoRoot, "node_modules", "typescript", "package.json"), "utf8"), -).version as string; -const oldApiSourceFiles = [createSourceFile(oldApiPath)]; -const newApiProgram = createNewApiProgram(); -const newApiChecker = newApiProgram.getTypeChecker(); -const newApiExports = getEntryPointExports(newApiProgram, newApiChecker, newApiEntryPoints); - -const oldApiTypes = getTypesWithMethods(oldApiSourceFiles, visitOld); -oldApiTypes.set("ts", getNamespaceFunctions(oldApiSourceFiles, "ts")); - -const newApiTypes = getExportedTypesWithMethods(newApiExports); -newApiTypes.set("ts", [getExportedTopLevelFunctions(newApiExports, newApiChecker), new Set()]); - -const acceptedDiffs = getAcceptedDiffs(); - -const report: string[] = [ - "# This report is generated by tools/scripts/gen/apidiff.mts.", - `# Old API: TypeScript ${typescriptVersion} (node_modules/typescript/lib/typescript.d.ts)`, - "# New API: public package entry points", -]; - -const comparisons = [...typeMappings].map(([oldType, newType]) => { - const oldMethods = oldApiTypes.get(oldType)!; - const newMethods = newApiTypes.get(newType)!; - const acceptedChanges = acceptedDiffs.get(oldType) ?? new Map(); - const present: string[] = []; - const missing: string[] = []; - for (const method of oldMethods) { - if (newMethods[0].has(method)) { - present.push(method); - newMethods[0].delete(method); - newMethods[1].add(method); - } - else if (newMethods[1].has(method)) { - present.push(method); - } - else if (acceptedChanges.has(method)) { - present.push(method); - const [newType, newMethodName] = acceptedChanges.get(method)!.split("."); - const actualNewType = newApiTypes.get(newType); - if (actualNewType) { - actualNewType[0].delete(newMethodName); - actualNewType[1].add(method); - } - } - else { - missing.push(method); - } - } - return { - oldType, - newType, - present: present.sort(), - missing: missing.sort(), - }; -}); - -report.push( - `Types not compared:`, -); -for (const [type, _] of oldApiTypes) { - typeMappings.has(type) || report.push(`- ${type}`); -} -report.push( - "", -); - -for (const comparison of comparisons) { - const { oldType, newType, present, missing } = comparison; - report.push( - "", - `# ${oldType} (old) methods compared to ${newType} (new).`, - `## (${present.length}) ${oldType} methods matched in async ${newType} ##`, - "", - `## ${oldType} methods missing from async ${newType} (${missing.length}) ##`, - ...missing, - "", - ); -} - -for (const [newType, newMethods] of newApiTypes) { - report.push( - `## New ${newType} methods in async API (${newMethods[0].size}) ##`, - ...newMethods[0], - "", - ); -} - -fs.writeFileSync(outputPath, report.join("\n")); - -function createSourceFile(file: string): ts.SourceFile { - return ts.createSourceFile( - file, - fs.readFileSync(file, "utf8"), - ts.ScriptTarget.Latest, - true, - ); -} - -function createNewApiProgram(): ts.Program { - const configPath = path.join(newApiPackageDirectory, "tsconfig.json"); - const configFile = ts.readConfigFile(configPath, ts.sys.readFile); - if (configFile.error) { - throw new Error(ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")); - } - const config = ts.parseJsonConfigFileContent(configFile.config, ts.sys, newApiPackageDirectory, { noEmit: true }, configPath); - if (config.errors.length > 0) { - throw new Error(config.errors.map(error => ts.flattenDiagnosticMessageText(error.messageText, "\n")).join("\n")); - } - return ts.createProgram({ - rootNames: config.fileNames, - options: config.options, - projectReferences: config.projectReferences, - }); -} - -type EntryPointExports = ReadonlyMap>; - -function getEntryPointExports(program: ts.Program, checker: ts.TypeChecker, entryPoints: readonly string[]): EntryPointExports { - const exports = new Map>(); - for (const entryPoint of entryPoints) { - const sourceFile = program.getSourceFile(entryPoint); - if (!sourceFile) throw new Error(`Could not find API entry point ${entryPoint}`); - const moduleSymbol = checker.getSymbolAtLocation(sourceFile); - if (!moduleSymbol) throw new Error(`Could not resolve API entry point ${entryPoint}`); - for (const exportedSymbol of checker.getExportsOfModule(moduleSymbol)) { - const symbols = exports.get(exportedSymbol.name) ?? new Set(); - symbols.add(resolveAlias(exportedSymbol, checker)); - exports.set(exportedSymbol.name, symbols); - } - } - return exports; -} - -function resolveAlias(symbol: ts.Symbol, checker: ts.TypeChecker): ts.Symbol { - return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; -} - -function getTypesWithMethods(sourceFiles: readonly ts.SourceFile[], visitNode: (node: ts.Node, types: Map) => void): Map { - const types = new Map(); - for (const sourceFile of sourceFiles) { - visit(sourceFile); - } - return types; - - function visit(node: ts.Node): void { - visitNode(node, types); - ts.forEachChild(node, visit); - } -} - -function getAcceptedDiffs(): Map> { - const diffs = new Map>(); - const contents = fs.readFileSync(path.join(repoRoot, "tsc", "testdata", "apiAccepted.txt"), "utf-8").split("\n"); - contents.slice(2).forEach(line => { - const [oldMethod, newMethod, _] = line.split("|"); - const [oldType, oldMethodName] = oldMethod.split("."); - if (!diffs.has(oldType)) diffs.set(oldType, new Map()); - diffs.get(oldType)!.set(oldMethodName, newMethod); - }); - return diffs; -} - -function visitOld(node: ts.Node, types: Map>): void { - if ((ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.name) { - const methods = types.get(node.name.text) ?? new Set(); - for (const member of node.members) { - if ( - (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) - && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) - && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) - ) { - const name = getPropertyName(member.name); - if (name !== undefined) methods.add(name); - } - } - if (methods.size > 0) types.set(node.name.text, methods); - } -} - -function getExportedTypesWithMethods(exports: EntryPointExports): Map, seen: Set]> { - const types = new Map, seen: Set]>(); - for (const [exportName, symbols] of exports) { - const methods = new Set(); - for (const symbol of symbols) { - for (const declaration of symbol.declarations ?? []) { - if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) { - addPublicMethods(declaration.members, methods); - } - } - } - if (methods.size > 0) types.set(exportName, [methods, new Set()]); - } - return types; -} - -function addPublicMethods(members: ts.NodeArray, methods: Set): void { - for (const member of members) { - if ( - (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) - && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) - && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) - ) { - const name = getPropertyName(member.name); - if (name !== undefined) methods.add(name); - } - } -} - -function getNamespaceFunctions(sourceFiles: readonly ts.SourceFile[], namespace: string): Set { - const functions = new Set(); - for (const sourceFile of sourceFiles) { - for (const statement of sourceFile.statements) { - if ( - ts.isModuleDeclaration(statement) - && statement.name.text === namespace - && statement.body - && ts.isModuleBlock(statement.body) - ) { - for (const member of statement.body.statements) { - if (ts.isFunctionDeclaration(member) && member.name) { - functions.add(member.name.text); - } - } - } - } - } - return functions; -} - -function getExportedTopLevelFunctions(exports: EntryPointExports, checker: ts.TypeChecker): Set { - const functions = new Set(); - for (const [exportName, symbols] of exports) { - for (const symbol of symbols) { - const declaration = symbol.valueDeclaration; - if ( - declaration - && checker.getTypeOfSymbolAtLocation(symbol, declaration).getCallSignatures().length > 0 - ) { - functions.add(exportName); - break; - } - } - } - return functions; -} - -function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean { - return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(modifier => modifier.kind === kind) === true; -} - -function getPropertyName(name: ts.PropertyName): string | undefined { - if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { - return name.text; - } - return undefined; -} diff --git a/tools/scripts/gen/apiold.mts b/tools/scripts/gen/apiold.mts deleted file mode 100644 index af6d92f2017c3..0000000000000 --- a/tools/scripts/gen/apiold.mts +++ /dev/null @@ -1,104 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import ts from "typescript"; -import { repoRoot } from "./utils.mts"; - -const oldApiPath = path.join(repoRoot, "node_modules", "typescript", "lib", "typescript.d.ts"); -const outputPath = path.join(repoRoot, "tsc", "testdata", "apiold.txt"); -const typescriptVersion = JSON.parse( - fs.readFileSync(path.join(repoRoot, "node_modules", "typescript", "package.json"), "utf8"), -).version as string; -const types = getTypesWithMethods(oldApiPath); - -const report = [ - "# Types in the old API that declare public methods.", - "# This report is generated by tools/scripts/gen/apiold.mts.", - `# Old API: TypeScript ${typescriptVersion} (node_modules/typescript/lib/typescript.d.ts)`, - "", -]; - -for (const [type, methods] of types) { - report.push(`## ${type} ##`, ...methods, ""); -} - -fs.writeFileSync(outputPath, report.join("\n")); - -function getTypesWithMethods(file: string): Map { - const sourceFile = ts.createSourceFile( - file, - fs.readFileSync(file, "utf8"), - ts.ScriptTarget.Latest, - true, - ); - const types = new Map>(); - visit(sourceFile, []); - - return new Map( - [...types] - .map(([type, methods]) => [type, [...methods].sort()] as const) - .sort(([left], [right]) => left.localeCompare(right)), - ); - - function visit(node: ts.Node, namespace: readonly string[]): void { - const childNamespace = ts.isModuleDeclaration(node) - ? [...namespace, getModuleName(node.name)] - : namespace; - const type = getTypeDeclaration(node); - if (type) { - const methods = getPublicMethods(type.members); - if (methods.size > 0) { - const qualifiedName = [...namespace, type.name.text].join("."); - const existing = types.get(qualifiedName); - if (existing) { - for (const method of methods) existing.add(method); - } - else { - types.set(qualifiedName, methods); - } - } - } - ts.forEachChild(node, child => visit(child, childNamespace)); - } -} - -type NamedTypeDeclaration = (ts.ClassDeclaration | ts.InterfaceDeclaration) & { name: ts.Identifier; }; - -function getTypeDeclaration(node: ts.Node): NamedTypeDeclaration | undefined { - if ( - (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) - && node.name - ) { - return node as NamedTypeDeclaration; - } - return undefined; -} - -function getPublicMethods(members: ts.NodeArray): Set { - const methods = new Set(); - for (const member of members) { - if ( - (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) - && !hasModifier(member, ts.SyntaxKind.PrivateKeyword) - && !hasModifier(member, ts.SyntaxKind.ProtectedKeyword) - ) { - const name = getPropertyName(member.name); - if (name !== undefined) methods.add(name); - } - } - return methods; -} - -function getModuleName(name: ts.ModuleName): string { - return name.text; -} - -function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean { - return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(modifier => modifier.kind === kind) === true; -} - -function getPropertyName(name: ts.PropertyName): string | undefined { - if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { - return name.text; - } - return undefined; -} diff --git a/tsc/internal/api/proto.go b/tsc/internal/api/proto.go index 84d034f63b1c3..da3a0f6a3f70f 100644 --- a/tsc/internal/api/proto.go +++ b/tsc/internal/api/proto.go @@ -1683,7 +1683,6 @@ type GetNonNullableTypeParams struct { Type TypeID `json:"type"` } -// GetNullableTypeParams are the parameters for the getNullableType method. type GetNullableTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` @@ -1691,21 +1690,18 @@ type GetNullableTypeParams struct { Flags int32 `json:"flags"` } -// GetStringLiteralTypeParams are the parameters for the getStringLiteralType method. type GetStringLiteralTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` Value string `json:"value"` } -// GetNumberLiteralTypeParams are the parameters for the getNumberLiteralType method. type GetNumberLiteralTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` Value float64 `json:"value"` } -// GetBigIntLiteralTypeParams are the parameters for the getBigIntLiteralType method. // Base10Value is the absolute value in base 10 with no leading zeros (empty for zero). type GetBigIntLiteralTypeParams struct { Snapshot SnapshotID `json:"snapshot"` @@ -1714,7 +1710,6 @@ type GetBigIntLiteralTypeParams struct { Base10Value string `json:"base10Value,omitempty"` } -// GetPrivateIdentifierPropertyOfTypeParams are the parameters for the getPrivateIdentifierPropertyOfType method. type GetPrivateIdentifierPropertyOfTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` @@ -1723,21 +1718,18 @@ type GetPrivateIdentifierPropertyOfTypeParams struct { Location NodeHandle `json:"location"` } -// GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. type GetTypeFromTypeNodeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` Location NodeHandle `json:"location"` } -// GetWidenedTypeParams are the parameters for the getWidenedType method. type GetWidenedTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` Type TypeID `json:"type"` } -// GetParameterTypeParams are the parameters for the getParameterType method. type GetParameterTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` @@ -1745,14 +1737,12 @@ type GetParameterTypeParams struct { Index int32 `json:"index"` } -// IsArrayLikeTypeParams checks whether a type is array-like. type IsArrayLikeTypeParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` Type TypeID `json:"type"` } -// IsTypeAssignableToParams checks assignability between two types. type IsTypeAssignableToParams struct { Snapshot SnapshotID `json:"snapshot"` Project project.Handle `json:"project"` diff --git a/tsc/internal/api/session.go b/tsc/internal/api/session.go index cb6799c551c77..aa2187f6335e4 100644 --- a/tsc/internal/api/session.go +++ b/tsc/internal/api/session.go @@ -3706,7 +3706,6 @@ func (s *Session) handleGetAwaitedType(ctx context.Context, params *CheckerTypeP return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetAwaitedType(t), setup.checker), nil } -// handleGetBaseTypeOfLiteralType returns the base type of a literal type (e.g. number for 42). func (s *Session) handleGetBaseTypeOfLiteralType(ctx context.Context, params *GetBaseTypeOfLiteralTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3738,7 +3737,6 @@ func (s *Session) handleGetNonNullableType(ctx context.Context, params *GetTypeP return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetNonNullableType(t), setup.checker), nil } -// handleGetNullableType adds undefined and/or null to a type if they are missing, as indicated by flags. func (s *Session) handleGetNullableType(ctx context.Context, params *GetNullableTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3755,7 +3753,6 @@ func (s *Session) handleGetNullableType(ctx context.Context, params *GetNullable return setup.sd.newTypeResponse(setup.projectID, result, setup.checker), nil } -// handleGetStringLiteralType returns (or creates) the singleton type for a string literal value. func (s *Session) handleGetStringLiteralType(ctx context.Context, params *GetStringLiteralTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3767,7 +3764,6 @@ func (s *Session) handleGetStringLiteralType(ctx context.Context, params *GetStr return setup.sd.newTypeResponse(setup.projectID, t, setup.checker), nil } -// handleGetNumberLiteralType returns (or creates) the singleton type for a numeric literal value. func (s *Session) handleGetNumberLiteralType(ctx context.Context, params *GetNumberLiteralTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3779,7 +3775,6 @@ func (s *Session) handleGetNumberLiteralType(ctx context.Context, params *GetNum return setup.sd.newTypeResponse(setup.projectID, t, setup.checker), nil } -// handleGetBigIntLiteralType returns (or creates) the singleton type for a bigint literal value. func (s *Session) handleGetBigIntLiteralType(ctx context.Context, params *GetBigIntLiteralTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3820,7 +3815,6 @@ func (s *Session) handleGetAugmentedPropertiesOfType(ctx context.Context, params return results, nil } -// handleGetJsxIntrinsicTagNamesAt returns all the properties of the Jsx.IntrinsicElements interface. // @gen-proto-nullable func (s *Session) handleGetJsxIntrinsicTagNamesAt(ctx context.Context, params *CheckerNodeParams) ([]*SymbolResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) @@ -3879,7 +3873,6 @@ func (s *Session) handleGetPrivateIdentifierPropertyOfType(ctx context.Context, return setup.newSymbolResponse(symbol), nil } -// handleGetTypeFromTypeNode returns the type for a type node. func (s *Session) handleGetTypeFromTypeNode(ctx context.Context, params *GetTypeFromTypeNodeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3895,7 +3888,6 @@ func (s *Session) handleGetTypeFromTypeNode(ctx context.Context, params *GetType return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetTypeFromTypeNode(node), setup.checker), nil } -// handleGetWidenedType returns the widened type. func (s *Session) handleGetWidenedType(ctx context.Context, params *GetWidenedTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3911,7 +3903,6 @@ func (s *Session) handleGetWidenedType(ctx context.Context, params *GetWidenedTy return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetWidenedType(t), setup.checker), nil } -// handleGetParameterType returns the type of a parameter at a given index in a signature. func (s *Session) handleGetParameterType(ctx context.Context, params *GetParameterTypeParams) (*TypeResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3948,7 +3939,6 @@ func (s *Session) handleGetTypeParameterAtPosition(ctx context.Context, params * return setup.sd.newTypeResponse(setup.projectID, setup.checker.GetTypeParameterAtPosition(sig, int(params.Index)), setup.checker), nil } -// handleIsArrayLikeType returns whether a type is array-like. func (s *Session) handleIsArrayLikeType(ctx context.Context, params *IsArrayLikeTypeParams) (bool, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -3964,7 +3954,6 @@ func (s *Session) handleIsArrayLikeType(ctx context.Context, params *IsArrayLike return setup.checker.IsArrayLikeType(t), nil } -// handleIsTypeAssignableTo returns whether source is assignable to target. func (s *Session) handleIsTypeAssignableTo(ctx context.Context, params *IsTypeAssignableToParams) (bool, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -4551,8 +4540,7 @@ func (s *Session) handleIsReadonlySymbol(ctx context.Context, params *CheckerSym return setup.checker.IsReadonlySymbol(symbol), nil } -// handleIsImplementationOfOverload returns whether a function-like declaration -// is the implementation signature of a set of overloads. +// handleIsImplementationOfOverload returns whether a function-like declaration is the implementation signature of a set of overloads. func (s *Session) handleIsImplementationOfOverload(ctx context.Context, params *CheckerNodeParams) (bool, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) if err != nil { @@ -4571,8 +4559,6 @@ func (s *Session) handleIsImplementationOfOverload(ctx context.Context, params * return setup.checker.NewEmitResolver(printer.NewEmitContext()).IsImplementationOfOverload(node), nil } -// handleGetPropertySymbolOfDestructuringAssignment returns the property symbol -// corresponding to an identifier in a destructuring assignment or for-of statement. // @gen-proto-nullable func (s *Session) handleGetPropertySymbolOfDestructuringAssignment(ctx context.Context, params *CheckerNodeParams) (*SymbolResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) @@ -4597,8 +4583,7 @@ func (s *Session) handleGetPropertySymbolOfDestructuringAssignment(ctx context.C return setup.newSymbolResponse(symbol), nil } -// handleGetAmbientModules returns all ambient modules declared in the program, -// such as `declare module "foo" { ... }`. +// handleGetAmbientModules returns all ambient modules declared in the program `declare module "foo" { ... }`. // @gen-proto-nullable func (s *Session) handleGetAmbientModules(ctx context.Context, params *GetIntrinsicTypeParams) ([]*SymbolResponse, error) { setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) From c87ea2788928007377234c70f50fd82a735141a8 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 29 Oct 2026 16:38:34 -0700 Subject: [PATCH 3/3] Add test cases Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- packages/typescript/test/sync/api-generators.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/typescript/test/sync/api-generators.test.ts b/packages/typescript/test/sync/api-generators.test.ts index d8cf33205ec2f..37f0f4e4e66ac 100644 --- a/packages/typescript/test/sync/api-generators.test.ts +++ b/packages/typescript/test/sync/api-generators.test.ts @@ -1728,6 +1728,15 @@ describe("API - generator batching", { concurrency: areTestsFiltered() }, () => parityCase("Checker", "getTypeArguments", checker.getTypeArguments, assertTypeArraysEquivalent, boxedType), parityCase("Checker", "getNonMissingTypeOfSymbol", checker.getNonMissingTypeOfSymbol, assertTypesEquivalent, boxedOptSymbol), parityCase("Checker", "isReadonlySymbol", checker.isReadonlySymbol, assertDeepEquivalent, boxedOptSymbol), + parityCase("Checker", "getNullableType", checker.getNullableType, assertTypesEquivalent, interfaceType, TypeFlags.Null), + parityCase("Checker", "getStringLiteralType", checker.getStringLiteralType, assertTypesEquivalent, "hello"), + parityCase("Checker", "getNumberLiteralType", checker.getNumberLiteralType, assertTypesEquivalent, 42), + parityCase("Checker", "getBigIntLiteralType", checker.getBigIntLiteralType, assertTypesEquivalent, 123n), + parityCase("Checker", "getFalseType", checker.getFalseType, assertTypesEquivalent), + parityCase("Checker", "getTrueType", checker.getTrueType, assertTypesEquivalent), + parityCase("Checker", "getAugmentedPropertiesOfType", checker.getAugmentedPropertiesOfType, assertSymbolArraysEquivalent, interfaceType), + parityCase("Checker", "getJsxIntrinsicTagNamesAt", checker.getJsxIntrinsicTagNamesAt, assertSymbolArraysEquivalent, combineDeclaration), + parityCase("Checker", "getPrivateIdentifierPropertyOfType", checker.getPrivateIdentifierPropertyOfType, assertOptionalSymbolsEquivalent, interfaceType, "#missing", combineDeclaration), parityCase("Checker", "isImplementationOfOverload", checker.isImplementationOfOverload, assertDeepEquivalent, combineDeclaration), parityCase("Checker", "getPropertySymbolOfDestructuringAssignment", checker.getPropertySymbolOfDestructuringAssignment, assertOptionalSymbolsEquivalent, importedDerived), parityCase("Checker", "getAmbientModules", checker.getAmbientModules, assertSymbolArraysEquivalent),