Skip to content

Commit 1878c5e

Browse files
authored
[api] Parse tsconfig plugins for external error reporting, expose MappedType properties (#64397)
1 parent c50e40d commit 1878c5e

14 files changed

Lines changed: 507 additions & 134 deletions

File tree

‎.github/skills/api-client/SKILL.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ description: Instructions for working on the API client in ./packages/typescript
77

88
The user may tell you to reference Strada (the old TypeScript codebase) for implementation, but your code comments should not acknowledge that this codebase is a port of anything else. Comments should stand on their own and describe the code in the current codebase. Never say "Mirrors TypeScript's 'foo' function" or "...like the classic API's 'Bar' type."
99

10+
## Adding properties/methods to `Type` and `Symbol`
11+
12+
When adding a property or method to `Type` or `Symbol` that references another `Type` or `Symbol`, decide how to represent it via this process:
13+
14+
- Does the public method take arguments, like `type.getProperty(name)`?
15+
- Add the method on `Type`
16+
- Add an equivalent method on `Checker`
17+
- Do not cache results on the `Type`.
18+
- Result: every call will perform a fetch.
19+
- Otherwise, does the method take non-trivial work to compute its result, like `type.getReducedType()`, or might it return a huge number of results, like `unionType.types()`?
20+
- Add the method on `Type`
21+
- Add an equivalent method on `Checker`
22+
- Do not precompute a result to include in `TypeResponse`
23+
- Do include a backing cache field on `Type`, initialized to `false`, and update it after the first call.
24+
- Result: the first call will always perform a fetch. Subsequent calls will use the cached result.
25+
- Otherwise, is the method trivial to compute, or represented as a plain property in the Go source?
26+
- Add the method on `Type`
27+
- Do not add an equivalent method on `Checker`
28+
- Precompute the reference and include the ID in `TypeResponse`
29+
- Result: the first call will not perform a fetch if a Type/Symbol by that identity is already in the object registry.
30+
31+
If the situation doesn't seem to fit any of these categories, STOP and ask the user for guidance, and suggest updating these instructions.
32+
1033
## Unnecessary spread and `exactOptionalPropertyTypes`
1134

1235
Never write code like `...(options ? { snapshot: options.snapshot } : {})`. All optional properties should tolerate `undefined`. If `--exactOptionalPropertyTypes` is forcing you to write code this way, fix the offending type. Rewriting as `if (options) foo.snapshot = options.snapshot` is not an acceptable workaround; always fix the type. As a rule, object types used in input positions should always define optional properties with `prop?: T | undefined` so this isn't a problem.

‎packages/typescript/src/api/async/api.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import type {
136136
IntrinsicType,
137137
JSDocTagInfo,
138138
LiteralType,
139+
MappedType,
139140
NumberLiteralType,
140141
ObjectType,
141142
StringLiteralType,
@@ -199,6 +200,7 @@ export type {
199200
LanguageServerSnapshotChanges,
200201
LiteralType,
201202
LSPConnectionOptions,
203+
MappedType,
202204
NumberLiteralType,
203205
ObjectType,
204206
PackageId,
@@ -2822,6 +2824,10 @@ class TypeObject implements Type {
28222824
readonly extendsType!: number;
28232825
readonly baseType!: number;
28242826
readonly substConstraint!: number;
2827+
readonly typeParameter!: number;
2828+
readonly constraintType!: number;
2829+
readonly nameType!: number;
2830+
readonly templateType!: number;
28252831

28262832
// Cached results of lazy fetches, not included in TypeResponse
28272833
// (typically because they require some amount of computation or
@@ -2901,6 +2907,10 @@ class TypeObject implements Type {
29012907
if (data.extendsType !== undefined) this.extendsType = data.extendsType;
29022908
if (data.baseType !== undefined) this.baseType = data.baseType;
29032909
if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint;
2910+
if (data.typeParameter !== undefined) this.typeParameter = data.typeParameter;
2911+
if (data.constraintType !== undefined) this.constraintType = data.constraintType;
2912+
if (data.nameType !== undefined) this.nameType = data.nameType;
2913+
if (data.templateType !== undefined) this.templateType = data.templateType;
29042914

29052915
this.trueType = false;
29062916
this.falseType = false;
@@ -3050,6 +3060,22 @@ class TypeObject implements Type {
30503060
return this.objectRegistry.fetchTypes(this, "getAliasTypeArgumentsOfType", this.aliasTypeArguments);
30513061
}
30523062

3063+
async getTypeParameter(): Promise<TypeParameter> {
3064+
return this.objectRegistry.fetchType(this, "getTypeParameterOfMappedType", this.typeParameter);
3065+
}
3066+
3067+
async getConstraintType(): Promise<Type> {
3068+
return this.objectRegistry.fetchType(this, "getConstraintTypeOfMappedType", this.constraintType);
3069+
}
3070+
3071+
async getNameType(): Promise<Type | undefined> {
3072+
return this.objectRegistry.fetchOptionalType(this, "getNameTypeOfMappedType", this.nameType);
3073+
}
3074+
3075+
async getTemplateType(): Promise<Type> {
3076+
return this.objectRegistry.fetchType(this, "getTemplateTypeOfMappedType", this.templateType);
3077+
}
3078+
30533079
async getObjectType(): Promise<Type> {
30543080
return this.objectRegistry.fetchType(this, "getObjectTypeOfType", this.objectType);
30553081
}
@@ -3196,6 +3222,10 @@ class TypeObject implements Type {
31963222
isTypeParameter(): this is TypeParameter {
31973223
return isTypeParameter(this);
31983224
}
3225+
3226+
isMappedType(): this is MappedType {
3227+
return !!(this.flags & TypeFlags.Object) && !!(this.objectFlags & ObjectFlags.Mapped);
3228+
}
31993229
}
32003230

32013231
export function isUnionType(type: Type): type is UnionType {

‎packages/typescript/src/api/async/types.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ export interface Type {
132132
isStringMappingType(): this is StringMappingType;
133133
/** Whether this type is a type parameter */
134134
isTypeParameter(): this is TypeParameter;
135+
/** Whether this is a mapped type */
136+
isMappedType(): this is MappedType;
135137
}
136138

137139
/**
@@ -180,6 +182,18 @@ export interface ObjectType extends Type {
180182
readonly objectFlags: ObjectFlags;
181183
}
182184

185+
/** Mapped types (ObjectFlags.Mapped) */
186+
export interface MappedType extends ObjectType {
187+
/** Get the type parameter iterated by the mapped type */
188+
getTypeParameter(): Promise<TypeParameter>;
189+
/** Get the constraint over which the mapped type iterates */
190+
getConstraintType(): Promise<Type>;
191+
/** Get the remapped property name type, if present */
192+
getNameType(): Promise<Type | undefined>;
193+
/** Get the property value template type */
194+
getTemplateType(): Promise<Type>;
195+
}
196+
183197
/** Type references (ObjectFlags.Reference) — e.g. Array<string>, Map<K, V> */
184198
export interface TypeReference extends ObjectType {
185199
/** Get the generic target type (e.g. Array for Array<string>) */

‎packages/typescript/src/api/proto.generated.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ export interface APIMethodInfo {
8787
getExtendsTypeOfType: APIMethod<GetTypePropertyParams, TypeResponse>;
8888
getBaseTypeOfType: APIMethod<GetTypePropertyParams, TypeResponse>;
8989
getConstraintOfType: APIMethod<GetTypePropertyParams, TypeResponse>;
90+
getTypeParameterOfMappedType: APIMethod<GetTypePropertyParams, TypeResponse>;
91+
getConstraintTypeOfMappedType: APIMethod<GetTypePropertyParams, TypeResponse>;
92+
getNameTypeOfMappedType: APIMethod<GetTypePropertyParams, TypeResponse | null>;
93+
getTemplateTypeOfMappedType: APIMethod<GetTypePropertyParams, TypeResponse>;
9094
getTypeParametersOfSignature: APIMethod<GetSignaturePropertyParams, TypeResponse[] | null>;
9195
getParametersOfSignature: APIMethod<GetSignaturePropertyParams, SymbolResponse[] | null>;
9296
getThisParameterOfSignature: APIMethod<GetSignaturePropertyParams, SymbolResponse | null>;
@@ -425,6 +429,11 @@ export interface TypeResponse {
425429
/** SubstitutionType data */
426430
baseType?: number | undefined;
427431
substConstraint?: number | undefined;
432+
/** MappedType data */
433+
typeParameter?: number | undefined;
434+
constraintType?: number | undefined;
435+
nameType?: number | undefined;
436+
templateType?: number | undefined;
428437
/** TemplateLiteralType text segments */
429438
texts?: string[] | undefined;
430439
/** FreshableType data (LiteralType and computed enum types) */
@@ -1027,6 +1036,7 @@ export interface BatchRequest {
10271036
| "getConstantValue"
10281037
| "getConstraintOfType"
10291038
| "getConstraintOfTypeParameter"
1039+
| "getConstraintTypeOfMappedType"
10301040
| "getContextualType"
10311041
| "getContextualTypeForArgument"
10321042
| "getCurrentLanguageServerSnapshot"
@@ -1060,6 +1070,7 @@ export interface BatchRequest {
10601070
| "getMembersOfSymbol"
10611071
| "getModeForResolutionAtIndex"
10621072
| "getModeForUsageLocation"
1073+
| "getNameTypeOfMappedType"
10631074
| "getNeverType"
10641075
| "getNonMissingTypeOfSymbol"
10651076
| "getNonNullableType"
@@ -1107,6 +1118,7 @@ export interface BatchRequest {
11071118
| "getTargetOfSignature"
11081119
| "getTargetOfType"
11091120
| "getTargetSymbol"
1121+
| "getTemplateTypeOfMappedType"
11101122
| "getThisParameterOfSignature"
11111123
| "getThisTypeOfType"
11121124
| "getTrueTypeOfConditionalType"
@@ -1119,6 +1131,7 @@ export interface BatchRequest {
11191131
| "getTypeOfSymbol"
11201132
| "getTypeOfSymbolAtLocation"
11211133
| "getTypeParameterAtPosition"
1134+
| "getTypeParameterOfMappedType"
11221135
| "getTypeParametersOfSignature"
11231136
| "getTypeParametersOfType"
11241137
| "getTypePredicateOfSignature"
@@ -1189,6 +1202,7 @@ export interface BatchResponse {
11891202
| "getConstantValue"
11901203
| "getConstraintOfType"
11911204
| "getConstraintOfTypeParameter"
1205+
| "getConstraintTypeOfMappedType"
11921206
| "getContextualType"
11931207
| "getContextualTypeForArgument"
11941208
| "getCurrentLanguageServerSnapshot"
@@ -1222,6 +1236,7 @@ export interface BatchResponse {
12221236
| "getMembersOfSymbol"
12231237
| "getModeForResolutionAtIndex"
12241238
| "getModeForUsageLocation"
1239+
| "getNameTypeOfMappedType"
12251240
| "getNeverType"
12261241
| "getNonMissingTypeOfSymbol"
12271242
| "getNonNullableType"
@@ -1269,6 +1284,7 @@ export interface BatchResponse {
12691284
| "getTargetOfSignature"
12701285
| "getTargetOfType"
12711286
| "getTargetSymbol"
1287+
| "getTemplateTypeOfMappedType"
12721288
| "getThisParameterOfSignature"
12731289
| "getThisTypeOfType"
12741290
| "getTrueTypeOfConditionalType"
@@ -1281,6 +1297,7 @@ export interface BatchResponse {
12811297
| "getTypeOfSymbol"
12821298
| "getTypeOfSymbolAtLocation"
12831299
| "getTypeParameterAtPosition"
1300+
| "getTypeParameterOfMappedType"
12841301
| "getTypeParametersOfSignature"
12851302
| "getTypeParametersOfType"
12861303
| "getTypePredicateOfSignature"
@@ -1494,6 +1511,8 @@ export interface CompilerOptions {
14941511
noUncheckedSideEffectImports?: boolean | undefined;
14951512
outDir?: string | undefined;
14961513
paths?: Record<string, string[]> | undefined;
1514+
/** Plugins are parsed only so tools can report that native TypeScript does not support them. */
1515+
plugins?: PluginImport[] | undefined;
14971516
preserveConstEnums?: boolean | undefined;
14981517
preserveSymlinks?: boolean | undefined;
14991518
project?: string | undefined;
@@ -1646,6 +1665,10 @@ export interface OpenedFileOperationResult {
16461665
project: ProjectId;
16471666
}
16481667

1668+
export interface PluginImport {
1669+
name: string;
1670+
}
1671+
16491672
/** CompletionEntryLabelDetailsResponse holds additional label display text for a completion entry. */
16501673
export interface CompletionEntryLabelDetailsResponse {
16511674
detail?: string | undefined;

‎packages/typescript/src/api/sync/api.ts‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ import type {
153153
IntrinsicType,
154154
JSDocTagInfo,
155155
LiteralType,
156+
MappedType,
156157
NumberLiteralType,
157158
ObjectType,
158159
StringLiteralType,
@@ -216,6 +217,7 @@ export type {
216217
LanguageServerSnapshotChanges,
217218
LiteralType,
218219
LSPConnectionOptions,
220+
MappedType,
219221
NumberLiteralType,
220222
ObjectType,
221223
PackageId,
@@ -6034,6 +6036,10 @@ class TypeObject implements Type {
60346036
readonly extendsType!: number;
60356037
readonly baseType!: number;
60366038
readonly substConstraint!: number;
6039+
readonly typeParameter!: number;
6040+
readonly constraintType!: number;
6041+
readonly nameType!: number;
6042+
readonly templateType!: number;
60376043

60386044
// Cached results of lazy fetches, not included in TypeResponse
60396045
// (typically because they require some amount of computation or
@@ -6113,6 +6119,10 @@ class TypeObject implements Type {
61136119
if (data.extendsType !== undefined) this.extendsType = data.extendsType;
61146120
if (data.baseType !== undefined) this.baseType = data.baseType;
61156121
if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint;
6122+
if (data.typeParameter !== undefined) this.typeParameter = data.typeParameter;
6123+
if (data.constraintType !== undefined) this.constraintType = data.constraintType;
6124+
if (data.nameType !== undefined) this.nameType = data.nameType;
6125+
if (data.templateType !== undefined) this.templateType = data.templateType;
61166126

61176127
this.trueType = false;
61186128
this.falseType = false;
@@ -6609,6 +6619,74 @@ class TypeObject implements Type {
66096619
);
66106620
}
66116621

6622+
get getTypeParameter(): {
6623+
(): TypeParameter;
6624+
gen(): Generator<ProtocolRequest, TypeParameter, ProtocolResponse["result"]>;
6625+
} {
6626+
const owner = this;
6627+
return cacheGeneratorMethod(
6628+
owner,
6629+
"getTypeParameter",
6630+
function (): TypeParameter {
6631+
return owner.objectRegistry.fetchType(owner, "getTypeParameterOfMappedType", owner.typeParameter);
6632+
},
6633+
function* (): Generator<ProtocolRequest, TypeParameter, ProtocolResponse["result"]> {
6634+
return yield* owner.objectRegistry.fetchType.gen(owner, "getTypeParameterOfMappedType", owner.typeParameter);
6635+
},
6636+
);
6637+
}
6638+
6639+
get getConstraintType(): {
6640+
(): Type;
6641+
gen(): Generator<ProtocolRequest, Type, ProtocolResponse["result"]>;
6642+
} {
6643+
const owner = this;
6644+
return cacheGeneratorMethod(
6645+
owner,
6646+
"getConstraintType",
6647+
function (): Type {
6648+
return owner.objectRegistry.fetchType(owner, "getConstraintTypeOfMappedType", owner.constraintType);
6649+
},
6650+
function* (): Generator<ProtocolRequest, Type, ProtocolResponse["result"]> {
6651+
return yield* owner.objectRegistry.fetchType.gen(owner, "getConstraintTypeOfMappedType", owner.constraintType);
6652+
},
6653+
);
6654+
}
6655+
6656+
get getNameType(): {
6657+
(): Type | undefined;
6658+
gen(): Generator<ProtocolRequest, Type | undefined, ProtocolResponse["result"]>;
6659+
} {
6660+
const owner = this;
6661+
return cacheGeneratorMethod(
6662+
owner,
6663+
"getNameType",
6664+
function (): Type | undefined {
6665+
return owner.objectRegistry.fetchOptionalType(owner, "getNameTypeOfMappedType", owner.nameType);
6666+
},
6667+
function* (): Generator<ProtocolRequest, Type | undefined, ProtocolResponse["result"]> {
6668+
return yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getNameTypeOfMappedType", owner.nameType);
6669+
},
6670+
);
6671+
}
6672+
6673+
get getTemplateType(): {
6674+
(): Type;
6675+
gen(): Generator<ProtocolRequest, Type, ProtocolResponse["result"]>;
6676+
} {
6677+
const owner = this;
6678+
return cacheGeneratorMethod(
6679+
owner,
6680+
"getTemplateType",
6681+
function (): Type {
6682+
return owner.objectRegistry.fetchType(owner, "getTemplateTypeOfMappedType", owner.templateType);
6683+
},
6684+
function* (): Generator<ProtocolRequest, Type, ProtocolResponse["result"]> {
6685+
return yield* owner.objectRegistry.fetchType.gen(owner, "getTemplateTypeOfMappedType", owner.templateType);
6686+
},
6687+
);
6688+
}
6689+
66126690
get getObjectType(): {
66136691
(): Type;
66146692
gen(): Generator<ProtocolRequest, Type, ProtocolResponse["result"]>;
@@ -6904,6 +6982,10 @@ class TypeObject implements Type {
69046982
isTypeParameter(): this is TypeParameter {
69056983
return isTypeParameter(this);
69066984
}
6985+
6986+
isMappedType(): this is MappedType {
6987+
return !!(this.flags & TypeFlags.Object) && !!(this.objectFlags & ObjectFlags.Mapped);
6988+
}
69076989
}
69086990

69096991
export function isUnionType(type: Type): type is UnionType {

0 commit comments

Comments
 (0)