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
10 changes: 10 additions & 0 deletions packages/typescript/src/api/async/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,16 @@ export class Checker {
return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
}

async getTypeOfPropertyOfType(type: Type, name: string): Promise<Type | undefined> {
const data = await this.client.apiRequest("getTypeOfPropertyOfType", {
snapshot: this.snapshotId,
project: this.project.id,
type: type.id,
name,
});
return data ? this.objectRegistry.getOrCreateType(data) : undefined;
}

async getConstantValue(node: Node): Promise<string | number | undefined> {
const data = await this.client.apiRequest("getConstantValue", {
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 @@ -93,6 +93,7 @@ export interface APIMethodInfo {
getApparentType: APIMethod<GetTypePropertyParams, TypeResponse>;
getReducedType: APIMethod<GetTypePropertyParams, TypeResponse>;
getPropertyOfType: APIMethod<GetPropertyOfTypeParams, SymbolResponse | null>;
getTypeOfPropertyOfType: APIMethod<GetPropertyOfTypeParams, TypeResponse | null>;
getIndexInfosOfType: APIMethod<CheckerTypeParams, IndexInfoResponse[] | null>;
getConstraintOfTypeParameter: APIMethod<GetTypePropertyParams, TypeResponse | null>;
getDefaultFromTypeParameter: APIMethod<GetTypePropertyParams, TypeResponse | null>;
Expand Down
10 changes: 10 additions & 0 deletions packages/typescript/src/api/sync/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,16 @@ export class Checker {
return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
}

getTypeOfPropertyOfType(type: Type, name: string): Type | undefined {
const data = this.client.apiRequest("getTypeOfPropertyOfType", {
snapshot: this.snapshotId,
project: this.project.id,
type: type.id,
name,
});
return data ? this.objectRegistry.getOrCreateType(data) : undefined;
}

getConstantValue(node: Node): string | number | undefined {
const data = this.client.apiRequest("getConstantValue", {
snapshot: this.snapshotId,
Expand Down
57 changes: 57 additions & 0 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4145,6 +4145,63 @@ export declare const p: Person;
});
});

describe("Checker - getTypeOfPropertyOfType", () => {
test("returns the type of a named property", async () => {
const src = `
export interface Person {
name: string;
age: number;
}
export declare const p: Person;
`;
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": src,
});
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("p: Person"));
assert.ok(symbol);
const type = await project.checker.getTypeOfSymbol(symbol);
const nameType = await project.checker.getTypeOfPropertyOfType(type, "name");
assert.ok(nameType);
assert.ok(nameType.flags & TypeFlags.String, `Expected string, got flags ${nameType.flags}`);
const ageType = await project.checker.getTypeOfPropertyOfType(type, "age");
assert.ok(ageType);
assert.ok(ageType.flags & TypeFlags.Number, `Expected number, got flags ${ageType.flags}`);
}
finally {
await api.close();
}
});

test("returns undefined for a missing property", async () => {
const src = `
export interface Person {
name: string;
}
export declare const p: Person;
`;
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": src,
});
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("p: Person"));
assert.ok(symbol);
const type = await project.checker.getTypeOfSymbol(symbol);
const missing = await project.checker.getTypeOfPropertyOfType(type, "doesNotExist");
assert.equal(missing, undefined);
}
finally {
await api.close();
}
});
});

describe("Checker - getConstantValue", () => {
test("returns numeric value of an enum member", async () => {
const api = spawnAPI({
Expand Down
57 changes: 57 additions & 0 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4153,6 +4153,63 @@ export declare const p: Person;
});
});

describe("Checker - getTypeOfPropertyOfType", () => {
test("returns the type of a named property", () => {
const src = `
export interface Person {
name: string;
age: number;
}
export declare const p: Person;
`;
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": src,
});
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("p: Person"));
assert.ok(symbol);
const type = project.checker.getTypeOfSymbol(symbol);
const nameType = project.checker.getTypeOfPropertyOfType(type, "name");
assert.ok(nameType);
assert.ok(nameType.flags & TypeFlags.String, `Expected string, got flags ${nameType.flags}`);
const ageType = project.checker.getTypeOfPropertyOfType(type, "age");
assert.ok(ageType);
assert.ok(ageType.flags & TypeFlags.Number, `Expected number, got flags ${ageType.flags}`);
}
finally {
api.close();
}
});

test("returns undefined for a missing property", () => {
const src = `
export interface Person {
name: string;
}
export declare const p: Person;
`;
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": src,
});
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("p: Person"));
assert.ok(symbol);
const type = project.checker.getTypeOfSymbol(symbol);
const missing = project.checker.getTypeOfPropertyOfType(type, "doesNotExist");
assert.equal(missing, undefined);
}
finally {
api.close();
}
});
});

describe("Checker - getConstantValue", () => {
test("returns numeric value of an enum member", () => {
const api = spawnAPI({
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 @@ -151,6 +151,7 @@ const (
MethodGetApparentType Method = "getApparentType"
MethodGetReducedType Method = "getReducedType"
MethodGetPropertyOfType Method = "getPropertyOfType"
MethodGetTypeOfPropertyOfType Method = "getTypeOfPropertyOfType"
MethodGetIndexInfosOfType Method = "getIndexInfosOfType"
MethodGetConstraintOfTypeParameter Method = "getConstraintOfTypeParameter"
MethodGetDefaultFromTypeParameter Method = "getDefaultFromTypeParameter"
Expand Down Expand Up @@ -490,6 +491,7 @@ var unmarshalers = map[Method]func([]byte) (any, error){
MethodGetApparentType: unmarshallerFor[GetTypePropertyParams],
MethodGetReducedType: unmarshallerFor[GetTypePropertyParams],
MethodGetPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams],
MethodGetTypeOfPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams],
MethodGetIndexInfosOfType: unmarshallerFor[CheckerTypeParams],
MethodGetConstraintOfTypeParameter: unmarshallerFor[GetTypePropertyParams],
MethodGetBaseConstraintOfType: unmarshallerFor[CheckerTypeParams],
Expand Down
24 changes: 24 additions & 0 deletions tsc/internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json.
return s.handleGetReducedType(ctx, parsed.(*GetTypePropertyParams))
case string(MethodGetPropertyOfType):
return s.handleGetPropertyOfType(ctx, parsed.(*GetPropertyOfTypeParams))
case string(MethodGetTypeOfPropertyOfType):
return s.handleGetTypeOfPropertyOfType(ctx, parsed.(*GetPropertyOfTypeParams))
case string(MethodGetIndexInfosOfType):
return s.handleGetIndexInfosOfType(ctx, parsed.(*CheckerTypeParams))
case string(MethodGetConstraintOfTypeParameter):
Expand Down Expand Up @@ -3197,6 +3199,28 @@ func (s *Session) handleGetPropertyOfType(ctx context.Context, params *GetProper
return setup.newSymbolResponse(prop), nil
}

// handleGetTypeOfPropertyOfType returns the type of a named property, or nil if missing.
// @gen-proto-nullable
func (s *Session) handleGetTypeOfPropertyOfType(ctx context.Context, params *GetPropertyOfTypeParams) (*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
}

propType := setup.checker.GetTypeOfPropertyOfType(t, params.Name)
if propType == nil {
return nil, nil
}

return setup.newTypeResponse(propType), nil
}

// handleGetConstantValue returns the constant value of an enum member or const enum access.
// @gen-proto-nullable
func (s *Session) handleGetConstantValue(ctx context.Context, params *CheckerNodeParams) (any, error) {
Expand Down