mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Merge pull request #32178 from microsoft/improveTupleDestructuring
Simplify tuple destructuring logic
This commit is contained in:
@@ -723,6 +723,7 @@ namespace ts {
|
||||
NoIndexSignatures = 1 << 0,
|
||||
Writing = 1 << 1,
|
||||
CacheSymbol = 1 << 2,
|
||||
NoTupleBoundsCheck = 1 << 3,
|
||||
}
|
||||
|
||||
const enum CallbackCheck {
|
||||
@@ -5119,21 +5120,25 @@ namespace ts {
|
||||
}
|
||||
else if (isArrayLikeType(parentType)) {
|
||||
const indexType = getLiteralType(index);
|
||||
const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, declaration.name), declaration.name);
|
||||
const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0;
|
||||
const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags) || errorType, declaration.name);
|
||||
type = getFlowTypeOfDestructuring(declaration, declaredType);
|
||||
}
|
||||
else {
|
||||
type = elementType;
|
||||
}
|
||||
}
|
||||
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
|
||||
// undefined from the final type.
|
||||
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined)) {
|
||||
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
|
||||
if (!declaration.initializer) {
|
||||
return type;
|
||||
}
|
||||
return declaration.initializer && !getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration)) ?
|
||||
getUnionType([type, checkDeclarationInitializer(declaration)], UnionReduction.Subtype) :
|
||||
type;
|
||||
if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) {
|
||||
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
|
||||
// undefined from the final type.
|
||||
return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ?
|
||||
getTypeWithFacts(type, TypeFacts.NEUndefined) :
|
||||
type;
|
||||
}
|
||||
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), checkDeclarationInitializer(declaration)], UnionReduction.Subtype);
|
||||
}
|
||||
|
||||
function getTypeForDeclarationFromJSDocComment(declaration: Node) {
|
||||
@@ -10144,7 +10149,7 @@ namespace ts {
|
||||
propType;
|
||||
}
|
||||
if (everyType(objectType, isTupleType) && isNumericLiteralName(propName) && +propName >= 0) {
|
||||
if (accessNode && everyType(objectType, t => !(<TupleTypeReference>t).target.hasRestElement)) {
|
||||
if (accessNode && everyType(objectType, t => !(<TupleTypeReference>t).target.hasRestElement) && !(accessFlags & AccessFlags.NoTupleBoundsCheck)) {
|
||||
const indexNode = getIndexNodeForAccessExpression(accessNode);
|
||||
if (isTupleType(objectType)) {
|
||||
error(indexNode, Diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2,
|
||||
@@ -19218,26 +19223,7 @@ namespace ts {
|
||||
function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) {
|
||||
// Infer a tuple type when the contextual type is or contains a tuple-like type
|
||||
if (readonly || (contextualType && forEachType(contextualType, isTupleLikeType))) {
|
||||
const minLength = elementCount - (hasRestElement ? 1 : 0);
|
||||
const pattern = contextualType && contextualType.pattern;
|
||||
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
|
||||
// tuple type with the corresponding binding or assignment element types to make the lengths equal.
|
||||
if (!hasRestElement && pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) {
|
||||
const patternElements = (<BindingPattern | ArrayLiteralExpression>pattern).elements;
|
||||
for (let i = elementCount; i < patternElements.length; i++) {
|
||||
const e = patternElements[i];
|
||||
if (hasDefaultValue(e)) {
|
||||
elementTypes.push((<TypeReference>contextualType).typeArguments![i]);
|
||||
}
|
||||
else if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && (<BindingElement>e).dotDotDotToken || e.kind === SyntaxKind.SpreadElement)) {
|
||||
if (e.kind !== SyntaxKind.OmittedExpression) {
|
||||
error(e, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
|
||||
}
|
||||
elementTypes.push(strictNullChecks ? implicitNeverType : undefinedWideningType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return createTupleType(elementTypes, minLength, hasRestElement, readonly);
|
||||
return createTupleType(elementTypes, elementCount - (hasRestElement ? 1 : 0), hasRestElement, readonly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23766,8 +23752,10 @@ namespace ts {
|
||||
if (isArrayLikeType(sourceType)) {
|
||||
// We create a synthetic expression so that getIndexedAccessType doesn't get confused
|
||||
// when the element is a SyntaxKind.ElementAccessExpression.
|
||||
const elementType = getIndexedAccessType(sourceType, indexType, createSyntheticExpression(element, indexType));
|
||||
const type = getFlowTypeOfDestructuring(element, elementType);
|
||||
const accessFlags = hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0;
|
||||
const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType;
|
||||
const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType;
|
||||
const type = getFlowTypeOfDestructuring(element, assignedType);
|
||||
return checkDestructuringAssignment(element, type, checkMode);
|
||||
}
|
||||
return checkDestructuringAssignment(element, elementType, checkMode);
|
||||
@@ -24356,10 +24344,13 @@ namespace ts {
|
||||
function checkDeclarationInitializer(declaration: HasExpressionInitializer) {
|
||||
const initializer = getEffectiveInitializer(declaration)!;
|
||||
const type = getTypeOfExpression(initializer, /*cache*/ true);
|
||||
const padded = isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern &&
|
||||
isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ?
|
||||
padTupleType(type, declaration.name) : type;
|
||||
const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const ||
|
||||
isDeclarationReadonly(declaration) ||
|
||||
isTypeAssertion(initializer) ||
|
||||
isLiteralOfContextualType(type, getContextualType(initializer)) ? type : getWidenedLiteralType(type);
|
||||
isLiteralOfContextualType(padded, getContextualType(initializer)) ? padded : getWidenedLiteralType(padded);
|
||||
if (isInJSFile(declaration)) {
|
||||
if (widened.flags & TypeFlags.Nullable) {
|
||||
reportImplicitAny(declaration, anyType);
|
||||
@@ -24373,6 +24364,22 @@ namespace ts {
|
||||
return widened;
|
||||
}
|
||||
|
||||
function padTupleType(type: TupleTypeReference, pattern: ArrayBindingPattern) {
|
||||
const patternElements = pattern.elements;
|
||||
const arity = getTypeReferenceArity(type);
|
||||
const elementTypes = arity ? type.typeArguments!.slice() : [];
|
||||
for (let i = arity; i < patternElements.length; i++) {
|
||||
const e = patternElements[i];
|
||||
if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && e.dotDotDotToken)) {
|
||||
elementTypes.push(!isOmittedExpression(e) && hasDefaultValue(e) ? getTypeFromBindingElement(e, /*includePatternInType*/ false, /*reportErrors*/ false) : anyType);
|
||||
if (!isOmittedExpression(e) && !hasDefaultValue(e)) {
|
||||
reportImplicitAny(e, anyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return createTupleType(elementTypes, type.target.minLength, /*hasRestElement*/ false, type.target.readonly);
|
||||
}
|
||||
|
||||
function isLiteralOfContextualType(candidateType: Type, contextualType: Type | undefined): boolean {
|
||||
if (contextualType) {
|
||||
if (contextualType.flags & TypeFlags.UnionOrIntersection) {
|
||||
|
||||
Reference in New Issue
Block a user