Skip to content

Commit e1c5989

Browse files
committed
Show tuple labels and documentation in completions
1 parent bf7e5d6 commit e1c5989

12 files changed

+143
-54
lines changed

src/compiler/checker.ts

+58-27
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);
@@ -12082,15 +12082,8 @@ namespace ts {
1208212082
const lastElement = lastOrUndefined(node.elements);
1208312083
const restElement = lastElement && unwrapNamedTupleMember(lastElement).kind === SyntaxKind.RestType ? lastElement : undefined;
1208412084
const minLength = findLastIndex(node.elements, n => unwrapNamedTupleMember(n).kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
12085-
let missingName = false;
12086-
const names = map(node.elements, e => {
12087-
if (e.kind !== SyntaxKind.NamedTupleMember) {
12088-
missingName = true;
12089-
return escapeLeadingUnderscores("arg");
12090-
}
12091-
return (e as NamedTupleMember).name.escapedText;
12092-
});
12093-
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : names);
12085+
const missingName = some(node.elements, e => e.kind !== SyntaxKind.NamedTupleMember);
12086+
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : node.elements as readonly NamedTupleMember[]);
1209412087
}
1209512088

1209612089
// Return true if the given type reference node is directly aliased or if it needs to be deferred
@@ -12187,7 +12180,7 @@ namespace ts {
1218712180
//
1218812181
// Note that the generic type created by this function has no symbol associated with it. The same
1218912182
// is true for each of the synthesized type parameters.
12190-
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType {
12183+
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration)[] | undefined): TupleType {
1219112184
let typeParameters: TypeParameter[] | undefined;
1219212185
const properties: Symbol[] = [];
1219312186
const maxLength = hasRestElement ? arity - 1 : arity;
@@ -12198,6 +12191,7 @@ namespace ts {
1219812191
if (i < maxLength) {
1219912192
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0),
1220012193
"" + i as __String, readonly ? CheckFlags.Readonly : 0);
12194+
property.tupleLabelDeclaration = namedMemberDeclarations?.[i];
1220112195
property.type = typeParameter;
1220212196
properties.push(property);
1220312197
}
@@ -12227,25 +12221,25 @@ namespace ts {
1222712221
type.minLength = minLength;
1222812222
type.hasRestElement = hasRestElement;
1222912223
type.readonly = readonly;
12230-
type.associatedNames = associatedNames;
12224+
type.labeledElementDeclarations = namedMemberDeclarations;
1223112225
return type;
1223212226
}
1223312227

12234-
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType {
12235-
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
12228+
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]): GenericType {
12229+
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (namedMemberDeclarations && namedMemberDeclarations.length ? "," + map(namedMemberDeclarations, getNodeId).join(",") : "");
1223612230
let type = tupleTypes.get(key);
1223712231
if (!type) {
12238-
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames));
12232+
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, namedMemberDeclarations));
1223912233
}
1224012234
return type;
1224112235
}
1224212236

12243-
function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) {
12237+
function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]) {
1224412238
const arity = elementTypes.length;
1224512239
if (arity === 1 && hasRestElement) {
1224612240
return createArrayType(elementTypes[0], readonly);
1224712241
}
12248-
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames);
12242+
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, namedMemberDeclarations);
1224912243
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
1225012244
}
1225112245

@@ -12260,7 +12254,7 @@ namespace ts {
1226012254
Math.max(0, tuple.minLength - index),
1226112255
tuple.hasRestElement,
1226212256
tuple.readonly,
12263-
tuple.associatedNames && tuple.associatedNames.slice(index),
12257+
tuple.labeledElementDeclarations && tuple.labeledElementDeclarations.slice(index),
1226412258
);
1226512259
}
1226612260

@@ -14260,7 +14254,7 @@ namespace ts {
1426014254
minLength;
1426114255
const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
1426214256
return contains(elementTypes, errorType) ? errorType :
14263-
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
14257+
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.labeledElementDeclarations);
1426414258
}
1426514259

1426614260
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
@@ -18517,7 +18511,7 @@ namespace ts {
1851718511
const elementTypes = map(getTypeArguments(source), t => inferReverseMappedType(t, target, constraint));
1851818512
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
1851918513
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
18520-
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames);
18514+
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.labeledElementDeclarations);
1852118515
}
1852218516
// For all other object types we infer a new object type where the reverse mapping has been
1852318517
// applied to the type of each property.
@@ -25114,7 +25108,7 @@ namespace ts {
2511425108
function getArrayifiedType(type: Type) {
2511525109
return type.flags & TypeFlags.Union ? mapType(type, getArrayifiedType) :
2511625110
type.flags & (TypeFlags.Any | TypeFlags.Instantiable) || isMutableArrayOrTuple(type) ? type :
25117-
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.associatedNames) :
25111+
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.labeledElementDeclarations) :
2511825112
createArrayType(getIndexedAccessType(type, numberType));
2511925113
}
2512025114

@@ -26974,6 +26968,11 @@ namespace ts {
2697426968
return type;
2697526969
}
2697626970

26971+
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember) {
26972+
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
26973+
return d.name.escapedText;
26974+
}
26975+
2697726976
function getParameterNameAtPosition(signature: Signature, pos: number) {
2697826977
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
2697926978
if (pos < paramCount) {
@@ -26982,13 +26981,33 @@ namespace ts {
2698226981
const restParameter = signature.parameters[paramCount] || unknownSymbol;
2698326982
const restType = getTypeOfSymbol(restParameter);
2698426983
if (isTupleType(restType)) {
26985-
const associatedNames = (<TupleType>(<TypeReference>restType).target).associatedNames;
26984+
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
2698626985
const index = pos - paramCount;
26987-
return associatedNames && associatedNames[index] || restParameter.escapedName + "_" + index as __String;
26986+
return associatedNames && getTupleElementLabel(associatedNames[index]) || restParameter.escapedName + "_" + index as __String;
2698826987
}
2698926988
return restParameter.escapedName;
2699026989
}
2699126990

26991+
function isValidDeclarationForTupleLabel(d: Declaration): d is NamedTupleMember | (ParameterDeclaration & { name: Identifier }) {
26992+
return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && isIdentifier(d.name));
26993+
}
26994+
26995+
function getNameableDeclarationAtPosition(signature: Signature, pos: number) {
26996+
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
26997+
if (pos < paramCount) {
26998+
const decl = signature.parameters[pos].valueDeclaration;
26999+
return decl && isValidDeclarationForTupleLabel(decl) ? decl : undefined;
27000+
}
27001+
const restParameter = signature.parameters[paramCount] || unknownSymbol;
27002+
const restType = getTypeOfSymbol(restParameter);
27003+
if (isTupleType(restType)) {
27004+
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
27005+
const index = pos - paramCount;
27006+
return associatedNames && associatedNames[index];
27007+
}
27008+
return restParameter.valueDeclaration && isValidDeclarationForTupleLabel(restParameter.valueDeclaration) ? restParameter.valueDeclaration : undefined;
27009+
}
27010+
2699227011
function getTypeAtPosition(signature: Signature, pos: number): Type {
2699327012
return tryGetTypeAtPosition(signature, pos) || anyType;
2699427013
}
@@ -27019,14 +27038,26 @@ namespace ts {
2701927038
return restType;
2702027039
}
2702127040
const types = [];
27022-
const names = [];
27041+
let names: (NamedTupleMember | ParameterDeclaration)[] | undefined = [];
2702327042
for (let i = pos; i < nonRestCount; i++) {
2702427043
types.push(getTypeAtPosition(source, i));
27025-
names.push(getParameterNameAtPosition(source, i));
27044+
const name = getNameableDeclarationAtPosition(source, i);
27045+
if (name && names) {
27046+
names.push(name);
27047+
}
27048+
else {
27049+
names = undefined;
27050+
}
2702627051
}
2702727052
if (restType) {
2702827053
types.push(getIndexedAccessType(restType, numberType));
27029-
names.push(getParameterNameAtPosition(source, nonRestCount));
27054+
const name = getNameableDeclarationAtPosition(source, nonRestCount);
27055+
if (name && names) {
27056+
names.push(name);
27057+
}
27058+
else {
27059+
names = undefined;
27060+
}
2703027061
}
2703127062
const minArgumentCount = getMinArgumentCount(source);
2703227063
const minLength = minArgumentCount < pos ? 0 : minArgumentCount - pos;

src/compiler/types.ts

+3-2
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

+8-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,14 @@ namespace ts {
325325
getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] {
326326
if (!this.documentationComment) {
327327
this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs
328-
this.documentationComment = getDocumentationComment(this.declarations, checker);
328+
329+
if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
330+
const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
331+
this.documentationComment = getDocumentationComment([labelDecl], checker);
332+
}
333+
else {
334+
this.documentationComment = getDocumentationComment(this.declarations, checker);
335+
}
329336
}
330337
return this.documentationComment;
331338
}

src/services/symbolDisplay.ts

+8
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

+2-2
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

+2-2
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

+2-2
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

+3-3
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)