Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/typescript/src/api/async/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,21 @@ export class Checker {
return this.objectRegistry.getOrCreateType(data);
}

/**
* Get the type of a symbol, excluding the missing type when
* `exactOptionalPropertyTypes: true` is set; for symbols whose
* type cannot be determined the checker yields the error type
* (use {@link Type.isErrorType} to detect it).
*/
async getNonMissingTypeOfSymbol(symbol: Symbol): Promise<Type> {
const data = await this.client.apiRequest("getNonMissingTypeOfSymbol", {
snapshot: this.snapshotId,
project: this.project.id,
symbol: symbol.id,
});
return this.objectRegistry.getOrCreateType(data);
}

async getReferencesToSymbolInFile(file: DocumentIdentifier, symbol: Symbol): Promise<NodeHandle[]> {
const data = await this.client.apiRequest("getReferencesToSymbolInFile", {
snapshot: this.snapshotId,
Expand Down
1 change: 1 addition & 0 deletions packages/typescript/src/api/proto.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface APIMethodInfo {
getTypeOfSymbol: APIMethod<GetTypeOfSymbolParams, TypeResponse>;
getTypesOfSymbols: APIMethod<GetTypesOfSymbolsParams, TypeResponse[]>;
getDeclaredTypeOfSymbol: APIMethod<GetTypeOfSymbolParams, TypeResponse>;
getNonMissingTypeOfSymbol: APIMethod<GetTypeOfSymbolParams, TypeResponse>;
getSourceFile: APIMethod<GetSourceFileParams, SourceFileResponse | null>;
getSourceFileNames: APIMethod<GetSourceFileNamesParams, string[]>;
getSourceFileMetadata: APIMethod<GetSourceFileParams, SourceFileMetadata | null>;
Expand Down
15 changes: 15 additions & 0 deletions packages/typescript/src/api/sync/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,21 @@ export class Checker {
return this.objectRegistry.getOrCreateType(data);
}

/**
* Get the type of a symbol, excluding the missing type when
* `exactOptionalPropertyTypes: true` is set; for symbols whose
* type cannot be determined the checker yields the error type
* (use {@link Type.isErrorType} to detect it).
*/
getNonMissingTypeOfSymbol(symbol: Symbol): Type {
const data = this.client.apiRequest("getNonMissingTypeOfSymbol", {
snapshot: this.snapshotId,
project: this.project.id,
symbol: symbol.id,
});
return this.objectRegistry.getOrCreateType(data);
}

getReferencesToSymbolInFile(file: DocumentIdentifier, symbol: Symbol): NodeHandle[] {
const data = this.client.apiRequest("getReferencesToSymbolInFile", {
snapshot: this.snapshotId,
Expand Down
56 changes: 56 additions & 0 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,62 @@ describe("Snapshot", () => {
await api.close();
}
});

test("getNonMissingTypeOfSymbol", async () => {
const api = spawnAPI({
"/tsconfig-one.json": JSON.stringify({
compilerOptions: {
exactOptionalPropertyTypes: true,
strict: true,
},
}),
"/tsconfig-two.json": JSON.stringify({
compilerOptions: {
exactOptionalPropertyTypes: false,
strict: true,
},
}),
"/src/index.ts": "const x: Partial<{ a: string }> = {};",
});

try {
// when `"exactOptionalPropertyTypes": true`
const snapshot1 = await api.updateSnapshot({ openProject: "/tsconfig-one.json" });
const project1 = snapshot1.getProject("/tsconfig-one.json")!;
const type1 = await project1.checker.getTypeAtPosition("/src/index.ts", 7);
assert.ok(type1);
const symbol1 = await project1.checker.getPropertyOfType(type1, "a");
assert.ok(symbol1);
const propertyType1 = await project1.checker.getTypeOfSymbol(symbol1);
assert.ok(propertyType1);
// getTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType1.isUnionType());
const propertyType2 = await project1.checker.getNonMissingTypeOfSymbol(symbol1);
assert.ok(propertyType2);
// getNonMissingTypeOfSymbol returns 'string'
assert.ok(!propertyType2.isUnionType());
assert.ok(propertyType2.flags & TypeFlags.String);

// when `"exactOptionalPropertyTypes": false`
const snapshot2 = await api.updateSnapshot({ openProject: "/tsconfig-two.json" });
const project2 = snapshot2.getProject("/tsconfig-two.json")!;
const type2 = await project2.checker.getTypeAtPosition("/src/index.ts", 7);
assert.ok(type2);
const symbol2 = await project2.checker.getPropertyOfType(type2, "a");
assert.ok(symbol2);
const propertyType3 = await project2.checker.getTypeOfSymbol(symbol2);
assert.ok(propertyType3);
// getTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType3.isUnionType());
const propertyType4 = await project2.checker.getNonMissingTypeOfSymbol(symbol2);
assert.ok(propertyType4);
// getNonMissingTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType4.isUnionType());
}
finally {
await api.close();
}
});
});

describe("LanguageService - imports", () => {
Expand Down
56 changes: 56 additions & 0 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,62 @@ describe("Snapshot", () => {
api.close();
}
});

test("getNonMissingTypeOfSymbol", () => {
const api = spawnAPI({
"/tsconfig-one.json": JSON.stringify({
compilerOptions: {
exactOptionalPropertyTypes: true,
strict: true,
},
}),
"/tsconfig-two.json": JSON.stringify({
compilerOptions: {
exactOptionalPropertyTypes: false,
strict: true,
},
}),
"/src/index.ts": "const x: Partial<{ a: string }> = {};",
});

try {
// when `"exactOptionalPropertyTypes": true`
const snapshot1 = api.updateSnapshot({ openProject: "/tsconfig-one.json" });
const project1 = snapshot1.getProject("/tsconfig-one.json")!;
const type1 = project1.checker.getTypeAtPosition("/src/index.ts", 7);
assert.ok(type1);
const symbol1 = project1.checker.getPropertyOfType(type1, "a");
assert.ok(symbol1);
const propertyType1 = project1.checker.getTypeOfSymbol(symbol1);
assert.ok(propertyType1);
// getTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType1.isUnionType());
const propertyType2 = project1.checker.getNonMissingTypeOfSymbol(symbol1);
assert.ok(propertyType2);
// getNonMissingTypeOfSymbol returns 'string'
assert.ok(!propertyType2.isUnionType());
assert.ok(propertyType2.flags & TypeFlags.String);

// when `"exactOptionalPropertyTypes": false`
const snapshot2 = api.updateSnapshot({ openProject: "/tsconfig-two.json" });
const project2 = snapshot2.getProject("/tsconfig-two.json")!;
const type2 = project2.checker.getTypeAtPosition("/src/index.ts", 7);
assert.ok(type2);
const symbol2 = project2.checker.getPropertyOfType(type2, "a");
assert.ok(symbol2);
const propertyType3 = project2.checker.getTypeOfSymbol(symbol2);
assert.ok(propertyType3);
// getTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType3.isUnionType());
const propertyType4 = project2.checker.getNonMissingTypeOfSymbol(symbol2);
assert.ok(propertyType4);
// getNonMissingTypeOfSymbol returns 'string | undefined'
assert.ok(propertyType4.isUnionType());
}
finally {
api.close();
}
});
});

describe("LanguageService - imports", () => {
Expand Down
2 changes: 2 additions & 0 deletions tsc/internal/api/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const (
MethodGetTypeOfSymbol Method = "getTypeOfSymbol"
MethodGetTypesOfSymbols Method = "getTypesOfSymbols"
MethodGetDeclaredTypeOfSymbol Method = "getDeclaredTypeOfSymbol"
MethodGetNonMissingTypeOfSymbol Method = "getNonMissingTypeOfSymbol"
MethodGetSourceFile Method = "getSourceFile"
MethodGetSourceFileNames Method = "getSourceFileNames"
MethodGetSourceFileMetadata Method = "getSourceFileMetadata"
Expand Down Expand Up @@ -428,6 +429,7 @@ var unmarshalers = map[Method]func([]byte) (any, error){
MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams],
MethodGetDeclaredTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
MethodGetNonMissingTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
MethodResolveName: unmarshallerFor[ResolveNameParams],
MethodGetSymbolsInScope: unmarshallerFor[GetSymbolsInScopeParams],
MethodGetSignaturesOfType: unmarshallerFor[GetSignaturesOfTypeParams],
Expand Down
18 changes: 18 additions & 0 deletions tsc/internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,8 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json.
return s.handleGetTypesOfSymbols(ctx, parsed.(*GetTypesOfSymbolsParams))
case string(MethodGetDeclaredTypeOfSymbol):
return s.handleGetDeclaredTypeOfSymbol(ctx, parsed.(*GetTypeOfSymbolParams))
case string(MethodGetNonMissingTypeOfSymbol):
return s.handleGetNonMissingTypeOfSymbol(ctx, parsed.(*GetTypeOfSymbolParams))
case string(MethodResolveName):
return s.handleResolveName(ctx, parsed.(*ResolveNameParams))
case string(MethodGetSymbolsInScope):
Expand Down Expand Up @@ -1642,6 +1644,22 @@ func (s *Session) handleGetDeclaredTypeOfSymbol(ctx context.Context, params *Get
return setup.newTypeResponse(setup.checker.GetDeclaredTypeOfSymbol(symbol)), nil
}

// handleGetNonMissingTypeOfSymbol returns the type of a symbol, excluding the missing type.
func (s *Session) handleGetNonMissingTypeOfSymbol(ctx context.Context, params *GetTypeOfSymbolParams) (*TypeResponse, error) {
setup, err := s.setupChecker(ctx, params.Snapshot, params.Project)
if err != nil {
return nil, err
}
defer setup.done()

symbol, err := setup.resolveSymbolHandle(params.Symbol)
if err != nil {
return nil, err
}

return setup.newTypeResponse(setup.checker.GetNonMissingTypeOfSymbol(symbol)), nil
}

// handleResolveName resolves a name to a symbol at a given location.
// @gen-proto-nullable
func (s *Session) handleResolveName(ctx context.Context, params *ResolveNameParams) (*SymbolResponse, error) {
Expand Down
4 changes: 4 additions & 0 deletions tsc/internal/checker/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ func (c *Checker) GetTypeOfSymbol(symbol *ast.Symbol) *Type {
return c.getTypeOfSymbol(symbol)
}

func (c *Checker) GetNonMissingTypeOfSymbol(symbol *ast.Symbol) *Type {
return c.getNonMissingTypeOfSymbol(symbol)
}

func (c *Checker) GetConstraintOfTypeParameter(typeParameter *Type) *Type {
return c.getConstraintOfTypeParameter(typeParameter)
}
Expand Down
Loading