Skip to content

Commit 4c50574

Browse files
committed
Show tuple labels and documentation in completions
1 parent 086e01f commit 4c50574

12 files changed

+143
-54
lines changed

src/compiler/checker.ts

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4568,9 +4568,9 @@ namespace ts {
45684568
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
45694569
createOptionalTypeNode(tupleConstituentNodes[i]);
45704570
}
4571-
if ((type.target as TupleType).associatedNames) {
4571+
if ((type.target as TupleType).labeledElementDeclarations) {
45724572
for (let i = 0; i < tupleConstituentNodes.length; i++) {
4573-
tupleConstituentNodes[i] = createNamedTupleMember(createIdentifier(unescapeLeadingUnderscores((type.target as TupleType).associatedNames![i])), tupleConstituentNodes[i]);
4573+
tupleConstituentNodes[i] = createNamedTupleMember(createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel((type.target as TupleType).labeledElementDeclarations![i]))), tupleConstituentNodes[i]);
45744574
}
45754575
}
45764576
const tupleTypeNode = setEmitFlags(createTupleTypeNode(tupleConstituentNodes), EmitFlags.SingleLine);
@@ -12104,15 +12104,8 @@ namespace ts {
1210412104
const lastElement = lastOrUndefined(node.elements);
1210512105
const restElement = lastElement && unwrapNamedTupleMember(lastElement).kind === SyntaxKind.RestType ? lastElement : undefined;
1210612106
const minLength = findLastIndex(node.elements, n => unwrapNamedTupleMember(n).kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
12107-
let missingName = false;
12108-
const names = map(node.elements, e => {
12109-
if (e.kind !== SyntaxKind.NamedTupleMember) {
12110-
missingName = true;
12111-
return escapeLeadingUnderscores("arg");
12112-
}
12113-
return (e as NamedTupleMember).name.escapedText;
12114-
});
12115-
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : names);
12107+
const missingName = some(node.elements, e => e.kind !== SyntaxKind.NamedTupleMember);
12108+
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : node.elements as readonly NamedTupleMember[]);
1211612109
}
1211712110

1211812111
// Return true if the given type reference node is directly aliased or if it needs to be deferred
@@ -12209,7 +12202,7 @@ namespace ts {
1220912202
//
1221012203
// Note that the generic type created by this function has no symbol associated with it. The same
1221112204
// is true for each of the synthesized type parameters.
12212-
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType {
12205+
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration)[] | undefined): TupleType {
1221312206
let typeParameters: TypeParameter[] | undefined;
1221412207
const properties: Symbol[] = [];
1221512208
const maxLength = hasRestElement ? arity - 1 : arity;
@@ -12220,6 +12213,7 @@ namespace ts {
1222012213
if (i < maxLength) {
1222112214
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0),
1222212215
"" + i as __String, readonly ? CheckFlags.Readonly : 0);
12216+
property.tupleLabelDeclaration = namedMemberDeclarations?.[i];
1222312217
property.type = typeParameter;
1222412218
properties.push(property);
1222512219
}
@@ -12249,25 +12243,25 @@ namespace ts {
1224912243
type.minLength = minLength;
1225012244
type.hasRestElement = hasRestElement;
1225112245
type.readonly = readonly;
12252-
type.associatedNames = associatedNames;
12246+
type.labeledElementDeclarations = namedMemberDeclarations;
1225312247
return type;
1225412248
}
1225512249

12256-
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType {
12257-
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
12250+
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]): GenericType {
12251+
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (namedMemberDeclarations && namedMemberDeclarations.length ? "," + map(namedMemberDeclarations, getNodeId).join(",") : "");
1225812252
let type = tupleTypes.get(key);
1225912253
if (!type) {
12260-
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames));
12254+
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, namedMemberDeclarations));
1226112255
}
1226212256
return type;
1226312257
}
1226412258

12265-
function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) {
12259+
function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]) {
1226612260
const arity = elementTypes.length;
1226712261
if (arity === 1 && hasRestElement) {
1226812262
return createArrayType(elementTypes[0], readonly);
1226912263
}
12270-
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames);
12264+
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, namedMemberDeclarations);
1227112265
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
1227212266
}
1227312267

@@ -12282,7 +12276,7 @@ namespace ts {
1228212276
Math.max(0, tuple.minLength - index),
1228312277
tuple.hasRestElement,
1228412278
tuple.readonly,
12285-
tuple.associatedNames && tuple.associatedNames.slice(index),
12279+
tuple.labeledElementDeclarations && tuple.labeledElementDeclarations.slice(index),
1228612280
);
1228712281
}
1228812282

@@ -14282,7 +14276,7 @@ namespace ts {
1428214276
minLength;
1428314277
const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
1428414278
return contains(elementTypes, errorType) ? errorType :
14285-
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
14279+
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.labeledElementDeclarations);
1428614280
}
1428714281

1428814282
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
@@ -18539,7 +18533,7 @@ namespace ts {
1853918533
const elementTypes = map(getTypeArguments(source), t => inferReverseMappedType(t, target, constraint));
1854018534
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
1854118535
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
18542-
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames);
18536+
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.labeledElementDeclarations);
1854318537
}
1854418538
// For all other object types we infer a new object type where the reverse mapping has been
1854518539
// applied to the type of each property.
@@ -25119,7 +25113,7 @@ namespace ts {
2511925113
function getArrayifiedType(type: Type) {
2512025114
return type.flags & TypeFlags.Union ? mapType(type, getArrayifiedType) :
2512125115
type.flags & (TypeFlags.Any | TypeFlags.Instantiable) || isMutableArrayOrTuple(type) ? type :
25122-
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.associatedNames) :
25116+
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.labeledElementDeclarations) :
2512325117
createArrayType(getIndexedAccessType(type, numberType));
2512425118
}
2512525119

@@ -26979,6 +26973,11 @@ namespace ts {
2697926973
return type;
2698026974
}
2698126975

26976+
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember) {
26977+
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
26978+
return d.name.escapedText;
26979+
}
26980+
2698226981
function getParameterNameAtPosition(signature: Signature, pos: number) {
2698326982
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
2698426983
if (pos < paramCount) {
@@ -26987,13 +26986,33 @@ namespace ts {
2698726986
const restParameter = signature.parameters[paramCount] || unknownSymbol;
2698826987
const restType = getTypeOfSymbol(restParameter);
2698926988
if (isTupleType(restType)) {
26990-
const associatedNames = (<TupleType>(<TypeReference>restType).target).associatedNames;
26989+
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
2699126990
const index = pos - paramCount;
26992-
return associatedNames && associatedNames[index] || restParameter.escapedName + "_" + index as __String;
26991+
return associatedNames && getTupleElementLabel(associatedNames[index]) || restParameter.escapedName + "_" + index as __String;
2699326992
}
2699426993
return restParameter.escapedName;
2699526994
}
2699626995

26996+
function isValidDeclarationForTupleLabel(d: Declaration): d is NamedTupleMember | (ParameterDeclaration & { name: Identifier }) {
26997+
return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && isIdentifier(d.name));
26998+
}
26999+
27000+
function getNameableDeclarationAtPosition(signature: Signature, pos: number) {
27001+
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
27002+
if (pos < paramCount) {
27003+
const decl = signature.parameters[pos].valueDeclaration;
27004+
return decl && isValidDeclarationForTupleLabel(decl) ? decl : undefined;
27005+
}
27006+
const restParameter = signature.parameters[paramCount] || unknownSymbol;
27007+
const restType = getTypeOfSymbol(restParameter);
27008+
if (isTupleType(restType)) {
27009+
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
27010+
const index = pos - paramCount;
27011+
return associatedNames && associatedNames[index];
27012+
}
27013+
return restParameter.valueDeclaration && isValidDeclarationForTupleLabel(restParameter.valueDeclaration) ? restParameter.valueDeclaration : undefined;
27014+
}
27015+
2699727016
function getTypeAtPosition(signature: Signature, pos: number): Type {
2699827017
return tryGetTypeAtPosition(signature, pos) || anyType;
2699927018
}
@@ -27024,14 +27043,26 @@ namespace ts {
2702427043
return restType;
2702527044
}
2702627045
const types = [];
27027-
const names = [];
27046+
let names: (NamedTupleMember | ParameterDeclaration)[] | undefined = [];
2702827047
for (let i = pos; i < nonRestCount; i++) {
2702927048
types.push(getTypeAtPosition(source, i));
27030-
names.push(getParameterNameAtPosition(source, i));
27049+
const name = getNameableDeclarationAtPosition(source, i);
27050+
if (name && names) {
27051+
names.push(name);
27052+
}
27053+
else {
27054+
names = undefined;
27055+
}
2703127056
}
2703227057
if (restType) {
2703327058
types.push(getIndexedAccessType(restType, numberType));
27034-
names.push(getParameterNameAtPosition(source, nonRestCount));
27059+
const name = getNameableDeclarationAtPosition(source, nonRestCount);
27060+
if (name && names) {
27061+
names.push(name);
27062+
}
27063+
else {
27064+
names = undefined;
27065+
}
2703527066
}
2703627067
const minArgumentCount = getMinArgumentCount(source);
2703727068
const minLength = minArgumentCount < pos ? 0 : minArgumentCount - pos;

src/compiler/types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ namespace ts {
12781278
elements: NodeArray<TypeNode | NamedTupleMember>;
12791279
}
12801280

1281-
export interface NamedTupleMember extends TypeNode, JSDocContainer {
1281+
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
12821282
kind: SyntaxKind.NamedTupleMember;
12831283
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
12841284
name: Identifier;
@@ -4170,6 +4170,7 @@ namespace ts {
41704170
cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target
41714171
typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
41724172
isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor
4173+
tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label
41734174
}
41744175

41754176
/* @internal */
@@ -4642,7 +4643,7 @@ namespace ts {
46424643
minLength: number;
46434644
hasRestElement: boolean;
46444645
readonly: boolean;
4645-
associatedNames?: __String[];
4646+
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
46464647
}
46474648

46484649
export interface TupleTypeReference extends TypeReference {

src/services/services.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,14 @@ namespace ts {
328328
getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] {
329329
if (!this.documentationComment) {
330330
this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs
331-
this.documentationComment = getDocumentationComment(this.declarations, checker);
331+
332+
if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
333+
const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
334+
this.documentationComment = getDocumentationComment([labelDecl], checker);
335+
}
336+
else {
337+
this.documentationComment = getDocumentationComment(this.declarations, checker);
338+
}
332339
}
333340
return this.documentationComment;
334341
}

src/services/symbolDisplay.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,14 @@ namespace ts.SymbolDisplay {
481481
else {
482482
addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration));
483483
}
484+
if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
485+
const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
486+
Debug.assertNode(labelDecl.name, isIdentifier);
487+
displayParts.push(spacePart());
488+
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
489+
displayParts.push(textPart(idText(labelDecl.name)));
490+
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
491+
}
484492
}
485493
else if (symbolFlags & SymbolFlags.Function ||
486494
symbolFlags & SymbolFlags.Method ||

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ declare namespace ts {
811811
kind: SyntaxKind.TupleType;
812812
elements: NodeArray<TypeNode | NamedTupleMember>;
813813
}
814-
export interface NamedTupleMember extends TypeNode, JSDocContainer {
814+
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
815815
kind: SyntaxKind.NamedTupleMember;
816816
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
817817
name: Identifier;
@@ -2483,7 +2483,7 @@ declare namespace ts {
24832483
minLength: number;
24842484
hasRestElement: boolean;
24852485
readonly: boolean;
2486-
associatedNames?: __String[];
2486+
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
24872487
}
24882488
export interface TupleTypeReference extends TypeReference {
24892489
target: TupleType;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ declare namespace ts {
811811
kind: SyntaxKind.TupleType;
812812
elements: NodeArray<TypeNode | NamedTupleMember>;
813813
}
814-
export interface NamedTupleMember extends TypeNode, JSDocContainer {
814+
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
815815
kind: SyntaxKind.NamedTupleMember;
816816
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
817817
name: Identifier;
@@ -2483,7 +2483,7 @@ declare namespace ts {
24832483
minLength: number;
24842484
hasRestElement: boolean;
24852485
readonly: boolean;
2486-
associatedNames?: __String[];
2486+
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
24872487
}
24882488
export interface TupleTypeReference extends TypeReference {
24892489
target: TupleType;

tests/baselines/reference/genericRestParameters1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ type T01 = Parameters<(x: number, y: string, z: boolean) => void>;
671671
>z : boolean
672672

673673
type T02 = Parameters<(...args: [number, string, boolean]) => void>;
674-
>T02 : [args_0: number, args_1: string, args_2: boolean]
674+
>T02 : [number, string, boolean]
675675
>args : [number, string, boolean]
676676

677677
type T03 = ConstructorParameters<new (x: number, y: string, z: boolean) => void>;
@@ -681,7 +681,7 @@ type T03 = ConstructorParameters<new (x: number, y: string, z: boolean) => void>
681681
>z : boolean
682682

683683
type T04 = ConstructorParameters<new (...args: [number, string, boolean]) => void>;
684-
>T04 : [args_0: number, args_1: string, args_2: boolean]
684+
>T04 : [number, string, boolean]
685685
>args : [number, string, boolean]
686686

687687
type T05<T> = Parameters<(...args: T[]) => void>;

tests/baselines/reference/genericRestParameters2.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ type T01 = Parameters<(x: number, y: string, ...z: boolean[]) => void>;
423423
>z : boolean[]
424424

425425
type T02 = Parameters<(...args: [number, string, ...boolean[]]) => void>;
426-
>T02 : [args_0: number, args_1: string, args_2: ...boolean[]]
426+
>T02 : [number, string, ...boolean[]]
427427
>args : [number, string, ...boolean[]]
428428

429429
type T03 = ConstructorParameters<new (x: number, y: string, ...z: boolean[]) => void>;
@@ -433,7 +433,7 @@ type T03 = ConstructorParameters<new (x: number, y: string, ...z: boolean[]) =>
433433
>z : boolean[]
434434

435435
type T04 = ConstructorParameters<new (...args: [number, string, ...boolean[]]) => void>;
436-
>T04 : [args_0: number, args_1: string, args_2: ...boolean[]]
436+
>T04 : [number, string, ...boolean[]]
437437
>args : [number, string, ...boolean[]]
438438

439439
type T05<T extends any[]> = Parameters<(x: string, ...args: T) => void>;
@@ -442,7 +442,7 @@ type T05<T extends any[]> = Parameters<(x: string, ...args: T) => void>;
442442
>args : T
443443

444444
type T06 = T05<[number, ...boolean[]]>;
445-
>T06 : [x: string, args_0: number, args_1: ...boolean[]]
445+
>T06 : [string, number, ...boolean[]]
446446

447447
type P1<T extends Function> = T extends (head: infer A, ...tail: infer B) => any ? { head: A, tail: B } : any[];
448448
>P1 : P1<T>

0 commit comments

Comments
 (0)