mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 01:04:49 -05:00
Derive tuple labels for rest elements from array binding patterns (#59045)
This commit is contained in:
@@ -13574,20 +13574,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly (readonly Symbol[])[] {
|
||||
if (signatureHasRestParameter(sig)) {
|
||||
const restIndex = sig.parameters.length - 1;
|
||||
const restName = sig.parameters[restIndex].escapedName;
|
||||
const restType = getTypeOfSymbol(sig.parameters[restIndex]);
|
||||
const restSymbol = sig.parameters[restIndex];
|
||||
const restType = getTypeOfSymbol(restSymbol);
|
||||
if (isTupleType(restType)) {
|
||||
return [expandSignatureParametersWithTupleMembers(restType, restIndex, restName)];
|
||||
return [expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)];
|
||||
}
|
||||
else if (!skipUnionExpanding && restType.flags & TypeFlags.Union && every((restType as UnionType).types, isTupleType)) {
|
||||
return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restName));
|
||||
return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restSymbol));
|
||||
}
|
||||
}
|
||||
return [sig.parameters];
|
||||
|
||||
function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restName: __String) {
|
||||
function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restSymbol: Symbol) {
|
||||
const elementTypes = getTypeArguments(restType);
|
||||
const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restName);
|
||||
const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restSymbol);
|
||||
const restParams = map(elementTypes, (t, i) => {
|
||||
// Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name
|
||||
const name = associatedNames && associatedNames[i] ? associatedNames[i] :
|
||||
@@ -13602,20 +13602,29 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return concatenate(sig.parameters.slice(0, restIndex), restParams);
|
||||
}
|
||||
|
||||
function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restName: __String) {
|
||||
const associatedNamesMap = new Map<__String, number>();
|
||||
return map(type.target.labeledElementDeclarations, (labeledElement, i) => {
|
||||
const name = getTupleElementLabel(labeledElement, i, restName);
|
||||
const prevCounter = associatedNamesMap.get(name);
|
||||
if (prevCounter === undefined) {
|
||||
associatedNamesMap.set(name, 1);
|
||||
return name;
|
||||
function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restSymbol: Symbol) {
|
||||
const names = map(type.target.labeledElementDeclarations, (labeledElement, i) => getTupleElementLabel(labeledElement, i, type.target.elementFlags[i], restSymbol));
|
||||
if (names) {
|
||||
const duplicates: number[] = [];
|
||||
const uniqueNames = new Set<__String>();
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
if (!tryAddToSet(uniqueNames, name)) {
|
||||
duplicates.push(i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
associatedNamesMap.set(name, prevCounter + 1);
|
||||
return `${name}_${prevCounter}` as __String;
|
||||
const counters = new Map<__String, number>();
|
||||
for (const i of duplicates) {
|
||||
let counter = counters.get(names[i]) ?? 1;
|
||||
let name: __String;
|
||||
while (!tryAddToSet(uniqueNames, name = `${names[i]}_${counter}` as __String)) {
|
||||
counter++;
|
||||
}
|
||||
names[i] = name;
|
||||
counters.set(names[i], counter + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37247,11 +37256,73 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a tuple element label by recursively walking `ArrayBindingPattern` nodes in a `BindingName`.
|
||||
* @param node The source node from which to derive a label
|
||||
* @param index The index into the tuple
|
||||
* @param elementFlags The {@see ElementFlags} of the tuple element
|
||||
*/
|
||||
function getTupleElementLabelFromBindingElement(node: BindingElement | ParameterDeclaration, index: number, elementFlags: ElementFlags): __String {
|
||||
switch (node.name.kind) {
|
||||
case SyntaxKind.Identifier: {
|
||||
const name = node.name.escapedText;
|
||||
if (node.dotDotDotToken) {
|
||||
// given
|
||||
// (...[x, y, ...z]: [number, number, ...number[]]) => ...
|
||||
// this produces
|
||||
// (x: number, y: number, ...z: number[]) => ...
|
||||
// which preserves rest elements of 'z'
|
||||
|
||||
// given
|
||||
// (...[x, y, ...z]: [number, number, ...[...number[], number]]) => ...
|
||||
// this produces
|
||||
// (x: number, y: number, ...z: number[], z_1: number) => ...
|
||||
// which preserves rest elements of z but gives distinct numbers to fixed elements of 'z'
|
||||
return elementFlags & ElementFlags.Variable ? name : `${name}_${index}` as __String;
|
||||
}
|
||||
else {
|
||||
// given
|
||||
// (...[x]: [number]) => ...
|
||||
// this produces
|
||||
// (x: number) => ...
|
||||
// which preserves fixed elements of 'x'
|
||||
|
||||
// given
|
||||
// (...[x]: ...number[]) => ...
|
||||
// this produces
|
||||
// (x_0: number) => ...
|
||||
// which which numbers fixed elements of 'x' whose tuple element type is variable
|
||||
return elementFlags & ElementFlags.Fixed ? name : `${name}_n` as __String;
|
||||
}
|
||||
}
|
||||
case SyntaxKind.ArrayBindingPattern: {
|
||||
if (node.dotDotDotToken) {
|
||||
const elements = node.name.elements;
|
||||
const lastElement = tryCast(lastOrUndefined(elements), isBindingElement);
|
||||
const elementCount = elements.length - (lastElement?.dotDotDotToken ? 1 : 0);
|
||||
if (index < elementCount) {
|
||||
const element = elements[index];
|
||||
if (isBindingElement(element)) {
|
||||
return getTupleElementLabelFromBindingElement(element, index, elementFlags);
|
||||
}
|
||||
}
|
||||
else if (lastElement?.dotDotDotToken) {
|
||||
return getTupleElementLabelFromBindingElement(lastElement, index - elementCount, elementFlags);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return `arg_${index}` as __String;
|
||||
}
|
||||
|
||||
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember): __String;
|
||||
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index: number, restParameterName?: __String): __String;
|
||||
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index?: number, restParameterName = "arg" as __String) {
|
||||
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index: number, elementFlags: ElementFlags, restSymbol?: Symbol): __String;
|
||||
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index = 0, elementFlags = ElementFlags.Fixed, restSymbol?: Symbol) {
|
||||
if (!d) {
|
||||
return `${restParameterName}_${index}` as __String;
|
||||
const restParameter = tryCast(restSymbol?.valueDeclaration, isParameter);
|
||||
return restParameter ? getTupleElementLabelFromBindingElement(restParameter, index, elementFlags) :
|
||||
`${restSymbol?.escapedName ?? "arg"}_${index}` as __String;
|
||||
}
|
||||
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
|
||||
return d.name.escapedText;
|
||||
@@ -37265,9 +37336,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const restParameter = signature.parameters[paramCount] || unknownSymbol;
|
||||
const restType = overrideRestType || getTypeOfSymbol(restParameter);
|
||||
if (isTupleType(restType)) {
|
||||
const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations;
|
||||
const tupleType = (restType as TypeReference).target as TupleType;
|
||||
const index = pos - paramCount;
|
||||
return getTupleElementLabel(associatedNames?.[index], index, restParameter.escapedName);
|
||||
const associatedName = tupleType.labeledElementDeclarations?.[index];
|
||||
const elementFlags = tupleType.elementFlags[index];
|
||||
return getTupleElementLabel(associatedName, index, elementFlags, restParameter);
|
||||
}
|
||||
return restParameter.escapedName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user