Skip to content
Merged
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 @@ -22,7 +22,9 @@ import {
type Identifier,
type IndexSignatureDeclaration,
ModifierFlags,
type NamedTupleMember,
type Node,
type ParameterDeclaration,
type Path,
type SourceFile,
type SyntaxKind,
Expand Down Expand Up @@ -724,6 +726,10 @@ class ProjectObjectRegistry {
return this.types.get(id);
}

createNodeHandle<T extends Node>(handle: string): NodeHandle<T> {
return new NodeHandle<T>(handle, this.project);
}

getOrCreateSignature(data: SignatureResponse): Signature {
let sig = this.signatures.get(data.id);
if (!sig) {
Expand Down Expand Up @@ -2427,6 +2433,7 @@ class TypeObject implements Type {
readonly elementFlags!: readonly ElementFlags[];
readonly fixedLength!: number;
readonly readonly!: boolean;
readonly labeledElementDeclarations?: readonly (NodeHandle<NamedTupleMember | ParameterDeclaration> | undefined)[];
readonly texts!: readonly string[];
readonly objectType!: number;
readonly indexType!: number;
Expand Down Expand Up @@ -2485,6 +2492,9 @@ class TypeObject implements Type {
this.fixedLength = data.fixedLength;
}
if (data.readonly !== undefined) this.readonly = data.readonly;
if (data.labeledElementDeclarations !== undefined) {
this.labeledElementDeclarations = data.labeledElementDeclarations.map(handle => handle ? objectRegistry.createNodeHandle<NamedTupleMember | ParameterDeclaration>(handle) : undefined);
}
if (data.texts !== undefined) this.texts = data.texts;
if (data.objectType !== undefined) this.objectType = data.objectType;
if (data.indexType !== undefined) this.indexType = data.indexType;
Expand Down
8 changes: 7 additions & 1 deletion packages/typescript/src/api/async/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import type { ElementFlags } from "#enums/elementFlags";
import type { ObjectFlags } from "#enums/objectFlags";
import type { TypeFlags } from "#enums/typeFlags";
import type { TypePredicateKind } from "#enums/typePredicateKind";
import type { IndexSignatureDeclaration } from "../../ast/ast.ts";
import type {
IndexSignatureDeclaration,
NamedTupleMember,
ParameterDeclaration,
} from "../../ast/ast.ts";
import type { Diagnostic } from "../proto.ts";
import type {
NodeHandle,
Expand Down Expand Up @@ -205,6 +209,8 @@ export interface TupleType extends InterfaceType {
readonly fixedLength: number;
/** Whether the tuple is readonly */
readonly readonly: boolean;
/** Declarations providing tuple element names */
readonly labeledElementDeclarations?: readonly (NodeHandle<NamedTupleMember | ParameterDeclaration> | undefined)[];
}

/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */
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 @@ -400,6 +400,7 @@ export interface TypeResponse {
elementFlags?: number[];
fixedLength?: number;
readonly?: boolean;
labeledElementDeclarations?: string[];
/** IndexedAccessType data */
objectType?: number;
indexType?: number;
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 @@ -39,7 +39,9 @@ import {
type Identifier,
type IndexSignatureDeclaration,
ModifierFlags,
type NamedTupleMember,
type Node,
type ParameterDeclaration,
type Path,
type SourceFile,
type SyntaxKind,
Expand Down Expand Up @@ -1212,6 +1214,10 @@ class ProjectObjectRegistry {
return this.types.get(id);
}

createNodeHandle<T extends Node>(handle: string): NodeHandle<T> {
return new NodeHandle<T>(handle, this.project);
}

getOrCreateSignature(data: SignatureResponse): Signature {
let sig = this.signatures.get(data.id);
if (!sig) {
Expand Down Expand Up @@ -5245,6 +5251,7 @@ class TypeObject implements Type {
readonly elementFlags!: readonly ElementFlags[];
readonly fixedLength!: number;
readonly readonly!: boolean;
readonly labeledElementDeclarations?: readonly (NodeHandle<NamedTupleMember | ParameterDeclaration> | undefined)[];
readonly texts!: readonly string[];
readonly objectType!: number;
readonly indexType!: number;
Expand Down Expand Up @@ -5303,6 +5310,9 @@ class TypeObject implements Type {
this.fixedLength = data.fixedLength;
}
if (data.readonly !== undefined) this.readonly = data.readonly;
if (data.labeledElementDeclarations !== undefined) {
this.labeledElementDeclarations = data.labeledElementDeclarations.map(handle => handle ? objectRegistry.createNodeHandle<NamedTupleMember | ParameterDeclaration>(handle) : undefined);
}
if (data.texts !== undefined) this.texts = data.texts;
if (data.objectType !== undefined) this.objectType = data.objectType;
if (data.indexType !== undefined) this.indexType = data.indexType;
Expand Down
8 changes: 7 additions & 1 deletion packages/typescript/src/api/sync/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import type { ElementFlags } from "#enums/elementFlags";
import type { ObjectFlags } from "#enums/objectFlags";
import type { TypeFlags } from "#enums/typeFlags";
import type { TypePredicateKind } from "#enums/typePredicateKind";
import type { IndexSignatureDeclaration } from "../../ast/ast.ts";
import type {
IndexSignatureDeclaration,
NamedTupleMember,
ParameterDeclaration,
} from "../../ast/ast.ts";
import type { Diagnostic } from "../proto.ts";
import type {
NodeHandle,
Expand Down Expand Up @@ -287,6 +291,8 @@ export interface TupleType extends InterfaceType {
readonly fixedLength: number;
/** Whether the tuple is readonly */
readonly readonly: boolean;
/** Declarations providing tuple element names */
readonly labeledElementDeclarations?: readonly (NodeHandle<NamedTupleMember | ParameterDeclaration> | undefined)[];
}

/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */
Expand Down
42 changes: 42 additions & 0 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3058,6 +3058,48 @@ array([]);
await api.close();
}
});

test("tuple targets expose labeled element declarations", async () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": `
export function gh1449<T extends [foo: any, bar?: any]>(a: T): T {
return a;
}
`,
});
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const sourceFile = await project.program.getSourceFile("/src/main.ts");
assert.ok(sourceFile);
const functionDeclaration = sourceFile.statements.find(isFunctionDeclaration);
assert.ok(functionDeclaration);
const constraint = functionDeclaration.typeParameters?.[0].constraint;
assert.ok(constraint);

const type = await project.checker.getTypeFromTypeNode(constraint);
assert.ok(type.isTupleType());
const target = await type.getTarget();
const declarations = target.labeledElementDeclarations;
assert.ok(declarations);
assert.equal(declarations.length, 2);

const names: string[] = [];
for (const handle of declarations) {
assert.ok(handle);
assert.equal(handle.kind, SyntaxKind.NamedTupleMember);
const declaration = await handle.resolve();
assert.ok(declaration);
assert.ok(isIdentifier(declaration.name));
names.push(declaration.name.text);
}
assert.deepEqual(names, ["foo", "bar"]);
}
finally {
await api.close();
}
});
});

describe("Checker - intrinsic type getters", () => {
Expand Down
42 changes: 42 additions & 0 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2974,6 +2974,48 @@ array([]);
api.close();
}
});

test("tuple targets expose labeled element declarations", () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
"/src/main.ts": `
export function gh1449<T extends [foo: any, bar?: any]>(a: T): T {
return a;
}
`,
});
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const sourceFile = project.program.getSourceFile("/src/main.ts");
assert.ok(sourceFile);
const functionDeclaration = sourceFile.statements.find(isFunctionDeclaration);
assert.ok(functionDeclaration);
const constraint = functionDeclaration.typeParameters?.[0].constraint;
assert.ok(constraint);

const type = project.checker.getTypeFromTypeNode(constraint);
assert.ok(type.isTupleType());
const target = type.getTarget();
const declarations = target.labeledElementDeclarations;
assert.ok(declarations);
assert.equal(declarations.length, 2);

const names: string[] = [];
for (const handle of declarations) {
assert.ok(handle);
assert.equal(handle.kind, SyntaxKind.NamedTupleMember);
const declaration = handle.resolve();
assert.ok(declaration);
assert.ok(isIdentifier(declaration.name));
names.push(declaration.name.text);
}
assert.deepEqual(names, ["foo", "bar"]);
}
finally {
api.close();
}
});
});

describe("Checker - intrinsic type getters", () => {
Expand Down
7 changes: 4 additions & 3 deletions tsc/internal/api/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -867,9 +867,10 @@ type TypeResponse struct {
LocalTypeParameters []TypeID `json:"localTypeParameters,omitempty"`

// TupleType data
ElementFlags []checker.ElementFlags `json:"elementFlags,omitempty"`
FixedLength *int `json:"fixedLength,omitempty"`
TupleReadonly *bool `json:"readonly,omitempty"`
ElementFlags []checker.ElementFlags `json:"elementFlags,omitempty"`
FixedLength *int `json:"fixedLength,omitempty"`
TupleReadonly *bool `json:"readonly,omitempty"`
LabeledElementDeclarations []NodeHandle `json:"labeledElementDeclarations,omitempty"`

// IndexedAccessType data
ObjectType TypeID `json:"objectType,omitzero"`
Expand Down
14 changes: 13 additions & 1 deletion tsc/internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,19 @@ func (sd *snapshotData) newTypeResponse(projectID ProjectID, t *checker.Type) *T
if t == nil {
return nil
}
return newTypeResponse(t, sd.registerType(projectID, t))
resp := newTypeResponse(t, sd.registerType(projectID, t))
if checker.IsTupleTypeTarget(t) {
elementInfos := t.AsTupleType().ElementInfos()
for i := range elementInfos {
if declaration := elementInfos[i].LabeledDeclaration(); declaration != nil {
if resp.LabeledElementDeclarations == nil {
resp.LabeledElementDeclarations = make([]NodeHandle, len(elementInfos))
}
resp.LabeledElementDeclarations[i] = sd.nodeHandleFrom(declaration)
Comment on lines +220 to +223
}
}
}
return resp
}

func (sd *snapshotData) registerType(projectID ProjectID, t *checker.Type) TypeID {
Expand Down