@@ -13574,20 +13574,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
13574
13574
function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly (readonly Symbol[])[] {
13575
13575
if (signatureHasRestParameter(sig)) {
13576
13576
const restIndex = sig.parameters.length - 1;
13577
- const restName = sig.parameters[restIndex].escapedName ;
13578
- const restType = getTypeOfSymbol(sig.parameters[restIndex] );
13577
+ const restSymbol = sig.parameters[restIndex];
13578
+ const restType = getTypeOfSymbol(restSymbol );
13579
13579
if (isTupleType(restType)) {
13580
- return [expandSignatureParametersWithTupleMembers(restType, restIndex, restName )];
13580
+ return [expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol )];
13581
13581
}
13582
13582
else if (!skipUnionExpanding && restType.flags & TypeFlags.Union && every((restType as UnionType).types, isTupleType)) {
13583
- return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restName ));
13583
+ return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restSymbol ));
13584
13584
}
13585
13585
}
13586
13586
return [sig.parameters];
13587
13587
13588
- function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restName: __String ) {
13588
+ function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restSymbol: Symbol ) {
13589
13589
const elementTypes = getTypeArguments(restType);
13590
- const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restName );
13590
+ const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restSymbol );
13591
13591
const restParams = map(elementTypes, (t, i) => {
13592
13592
// Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name
13593
13593
const name = associatedNames && associatedNames[i] ? associatedNames[i] :
@@ -13602,20 +13602,29 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
13602
13602
return concatenate(sig.parameters.slice(0, restIndex), restParams);
13603
13603
}
13604
13604
13605
- function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restName: __String) {
13606
- const associatedNamesMap = new Map<__String, number>();
13607
- return map(type.target.labeledElementDeclarations, (labeledElement, i) => {
13608
- const name = getTupleElementLabel(labeledElement, i, restName);
13609
- const prevCounter = associatedNamesMap.get(name);
13610
- if (prevCounter === undefined) {
13611
- associatedNamesMap.set(name, 1);
13612
- return name;
13605
+ function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restSymbol: Symbol) {
13606
+ const names = map(type.target.labeledElementDeclarations, (labeledElement, i) => getTupleElementLabel(labeledElement, i, type.target.elementFlags[i], restSymbol));
13607
+ if (names) {
13608
+ const duplicates: number[] = [];
13609
+ const uniqueNames = new Set<__String>();
13610
+ for (let i = 0; i < names.length; i++) {
13611
+ const name = names[i];
13612
+ if (!tryAddToSet(uniqueNames, name)) {
13613
+ duplicates.push(i);
13614
+ }
13613
13615
}
13614
- else {
13615
- associatedNamesMap.set(name, prevCounter + 1);
13616
- return `${name}_${prevCounter}` as __String;
13616
+ const counters = new Map<__String, number>();
13617
+ for (const i of duplicates) {
13618
+ let counter = counters.get(names[i]) ?? 1;
13619
+ let name: __String;
13620
+ while (!tryAddToSet(uniqueNames, name = `${names[i]}_${counter}` as __String)) {
13621
+ counter++;
13622
+ }
13623
+ names[i] = name;
13624
+ counters.set(names[i], counter + 1);
13617
13625
}
13618
- });
13626
+ }
13627
+ return names;
13619
13628
}
13620
13629
}
13621
13630
@@ -37247,11 +37256,73 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
37247
37256
);
37248
37257
}
37249
37258
37259
+ /**
37260
+ * Gets a tuple element label by recursively walking `ArrayBindingPattern` nodes in a `BindingName`.
37261
+ * @param node The source node from which to derive a label
37262
+ * @param index The index into the tuple
37263
+ * @param elementFlags The {@see ElementFlags} of the tuple element
37264
+ */
37265
+ function getTupleElementLabelFromBindingElement(node: BindingElement | ParameterDeclaration, index: number, elementFlags: ElementFlags): __String {
37266
+ switch (node.name.kind) {
37267
+ case SyntaxKind.Identifier: {
37268
+ const name = node.name.escapedText;
37269
+ if (node.dotDotDotToken) {
37270
+ // given
37271
+ // (...[x, y, ...z]: [number, number, ...number[]]) => ...
37272
+ // this produces
37273
+ // (x: number, y: number, ...z: number[]) => ...
37274
+ // which preserves rest elements of 'z'
37275
+
37276
+ // given
37277
+ // (...[x, y, ...z]: [number, number, ...[...number[], number]]) => ...
37278
+ // this produces
37279
+ // (x: number, y: number, ...z: number[], z_1: number) => ...
37280
+ // which preserves rest elements of z but gives distinct numbers to fixed elements of 'z'
37281
+ return elementFlags & ElementFlags.Variable ? name : `${name}_${index}` as __String;
37282
+ }
37283
+ else {
37284
+ // given
37285
+ // (...[x]: [number]) => ...
37286
+ // this produces
37287
+ // (x: number) => ...
37288
+ // which preserves fixed elements of 'x'
37289
+
37290
+ // given
37291
+ // (...[x]: ...number[]) => ...
37292
+ // this produces
37293
+ // (x_0: number) => ...
37294
+ // which which numbers fixed elements of 'x' whose tuple element type is variable
37295
+ return elementFlags & ElementFlags.Fixed ? name : `${name}_n` as __String;
37296
+ }
37297
+ }
37298
+ case SyntaxKind.ArrayBindingPattern: {
37299
+ if (node.dotDotDotToken) {
37300
+ const elements = node.name.elements;
37301
+ const lastElement = tryCast(lastOrUndefined(elements), isBindingElement);
37302
+ const elementCount = elements.length - (lastElement?.dotDotDotToken ? 1 : 0);
37303
+ if (index < elementCount) {
37304
+ const element = elements[index];
37305
+ if (isBindingElement(element)) {
37306
+ return getTupleElementLabelFromBindingElement(element, index, elementFlags);
37307
+ }
37308
+ }
37309
+ else if (lastElement?.dotDotDotToken) {
37310
+ return getTupleElementLabelFromBindingElement(lastElement, index - elementCount, elementFlags);
37311
+ }
37312
+ }
37313
+ break;
37314
+ }
37315
+ }
37316
+ return `arg_${index}` as __String;
37317
+ }
37318
+
37250
37319
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember): __String;
37251
- function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index: number, restParameterName ?: __String ): __String;
37252
- function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index?: number, restParameterName = "arg" as __String ) {
37320
+ function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index: number, elementFlags: ElementFlags, restSymbol ?: Symbol ): __String;
37321
+ function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index = 0, elementFlags = ElementFlags.Fixed, restSymbol?: Symbol ) {
37253
37322
if (!d) {
37254
- return `${restParameterName}_${index}` as __String;
37323
+ const restParameter = tryCast(restSymbol?.valueDeclaration, isParameter);
37324
+ return restParameter ? getTupleElementLabelFromBindingElement(restParameter, index, elementFlags) :
37325
+ `${restSymbol?.escapedName ?? "arg"}_${index}` as __String;
37255
37326
}
37256
37327
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
37257
37328
return d.name.escapedText;
@@ -37265,9 +37336,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
37265
37336
const restParameter = signature.parameters[paramCount] || unknownSymbol;
37266
37337
const restType = overrideRestType || getTypeOfSymbol(restParameter);
37267
37338
if (isTupleType(restType)) {
37268
- const associatedNames = (( restType as TypeReference).target as TupleType).labeledElementDeclarations ;
37339
+ const tupleType = (restType as TypeReference).target as TupleType;
37269
37340
const index = pos - paramCount;
37270
- return getTupleElementLabel(associatedNames?.[index], index, restParameter.escapedName);
37341
+ const associatedName = tupleType.labeledElementDeclarations?.[index];
37342
+ const elementFlags = tupleType.elementFlags[index];
37343
+ return getTupleElementLabel(associatedName, index, elementFlags, restParameter);
37271
37344
}
37272
37345
return restParameter.escapedName;
37273
37346
}
0 commit comments