mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 03:09:39 -06:00
Revert catastrophically bad fix and investigate root cause of last tuple element issue
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
parent
2d1cf66c34
commit
1f1fe0c217
@ -31857,10 +31857,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const parentType = getContextualTypeForVariableLikeDeclaration(parent, contextFlags) ||
|
||||
parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent, declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal);
|
||||
if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined;
|
||||
if (parent.name.kind === SyntaxKind.ArrayBindingPattern) {
|
||||
const index = indexOfNode(declaration.parent.elements, declaration);
|
||||
if (index < 0) return undefined;
|
||||
return getContextualTypeForElementExpression(parentType, index);
|
||||
if (parent.name.kind === SyntaxKind.ArrayBindingPattern) {
|
||||
const index = indexOfNode(declaration.parent.elements, declaration);
|
||||
if (index < 0) return undefined;
|
||||
return getContextualTypeForElementExpression(parentType, index, declaration.parent.elements.length);
|
||||
}
|
||||
const nameType = getLiteralTypeFromPropertyName(name);
|
||||
if (isTypeUsableAsPropertyName(nameType)) {
|
||||
@ -32391,25 +32391,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
function getContextualTypeForElementExpression(type: Type | undefined, index: number, length?: number, firstSpreadIndex?: number, lastSpreadIndex?: number): Type | undefined {
|
||||
return type && mapType(type, t => {
|
||||
if (isTupleType(t)) {
|
||||
// If index is before any spread element and within the fixed part of the contextual tuple type, return
|
||||
// the type of the contextual tuple element.
|
||||
if ((firstSpreadIndex === undefined || index < firstSpreadIndex) && index < t.target.fixedLength) {
|
||||
return removeMissingType(getTypeArguments(t)[index], !!(t.target.elementFlags[index] && ElementFlags.Optional));
|
||||
}
|
||||
// When the length is known and the index is after all spread elements we compute the offset from the element
|
||||
// to the end and the number of ending fixed elements in the contextual tuple type.
|
||||
const offset = length !== undefined && (lastSpreadIndex === undefined || index > lastSpreadIndex) ? length - index : 0;
|
||||
const fixedEndLength = offset > 0 && (t.target.combinedFlags & ElementFlags.Variable) ? getEndElementCount(t.target, ElementFlags.Fixed) : 0;
|
||||
// If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual
|
||||
// tuple element.
|
||||
if (offset > 0 && offset <= fixedEndLength) {
|
||||
return getTypeArguments(t)[getTypeReferenceArity(t) - offset];
|
||||
}
|
||||
// Return a union of the possible contextual element types with no subtype reduction.
|
||||
return getElementTypeOfSliceOfTupleType(t, firstSpreadIndex === undefined ? t.target.fixedLength : Math.min(t.target.fixedLength, firstSpreadIndex), length === undefined || lastSpreadIndex === undefined ? fixedEndLength : Math.min(fixedEndLength, length - lastSpreadIndex), /*writing*/ false, /*noReductions*/ true);
|
||||
function getContextualTypeForElementExpression(type: Type | undefined, index: number, length?: number, firstSpreadIndex?: number, lastSpreadIndex?: number): Type | undefined {
|
||||
return type && mapType(type, t => {
|
||||
if (isTupleType(t)) {
|
||||
// If index is before any spread element and within the fixed part of the contextual tuple type, return
|
||||
// the type of the contextual tuple element.
|
||||
if ((firstSpreadIndex === undefined || index < firstSpreadIndex) && index < t.target.fixedLength) {
|
||||
return removeMissingType(getTypeArguments(t)[index], !!(t.target.elementFlags[index] && ElementFlags.Optional));
|
||||
}
|
||||
// When the length is known and the index is after all spread elements we compute the offset from the element
|
||||
// to the end and the number of ending fixed elements in the contextual tuple type.
|
||||
const offset = length !== undefined && (lastSpreadIndex === undefined || index > lastSpreadIndex) ? length - index : 0;
|
||||
const fixedEndLength = offset > 0 && (t.target.combinedFlags & ElementFlags.Variable) ? getEndElementCount(t.target, ElementFlags.Fixed) : 0;
|
||||
// If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual
|
||||
// tuple element.
|
||||
if (offset > 0 && offset <= fixedEndLength) {
|
||||
return getTypeArguments(t)[getTypeReferenceArity(t) - offset];
|
||||
}
|
||||
// Return a union of the possible contextual element types with no subtype reduction.
|
||||
return getElementTypeOfSliceOfTupleType(t, firstSpreadIndex === undefined ? t.target.fixedLength : Math.min(t.target.fixedLength, firstSpreadIndex), length === undefined || lastSpreadIndex === undefined ? fixedEndLength : Math.min(fixedEndLength, length - lastSpreadIndex), /*writing*/ false, /*noReductions*/ true);
|
||||
}
|
||||
// If element index is known and a contextual property with that name exists, return it. Otherwise return the
|
||||
// iterated or element type of the contextual type.
|
||||
@ -36069,11 +36069,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive),
|
||||
// we obtain the regular type of any object literal arguments because we may not have inferred complete
|
||||
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
|
||||
// Also skip fresh literal checking when the call is in certain destructuring contexts that can cause
|
||||
// incorrect excess property errors (see #41548).
|
||||
const shouldSkipFreshness = (checkMode & CheckMode.SkipContextSensitive) ||
|
||||
(isCallExpression(node) && isCallInProblematicDestructuringContext(node));
|
||||
const checkArgType = shouldSkipFreshness ? getRegularTypeOfObjectLiteral(argType) : argType;
|
||||
const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType;
|
||||
const effectiveCheckArgumentNode = getEffectiveCheckNode(arg);
|
||||
if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? effectiveCheckArgumentNode : undefined, effectiveCheckArgumentNode, headMessage, containingMessageChain, errorOutputContainer)) {
|
||||
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors");
|
||||
@ -36421,24 +36417,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
|
||||
}
|
||||
|
||||
function isCallInProblematicDestructuringContext(node: CallLikeExpression): boolean {
|
||||
// Check if this call expression is used as the initializer in a variable declaration with a destructuring pattern
|
||||
const parent = node.parent;
|
||||
if (parent && isVariableDeclaration(parent) && parent.initializer === node) {
|
||||
if (isArrayBindingPattern(parent.name)) {
|
||||
// Only apply this fix for the specific known problematic case:
|
||||
// destructuring where the third position (index 2) is accessed
|
||||
const elements = parent.name.elements;
|
||||
return elements.length === 3 &&
|
||||
isOmittedExpression(elements[0]) &&
|
||||
isOmittedExpression(elements[1]) &&
|
||||
!isOmittedExpression(elements[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, headMessage?: DiagnosticMessage): Signature {
|
||||
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
|
||||
const isDecorator = node.kind === SyntaxKind.Decorator;
|
||||
|
||||
11
tests/cases/compiler/lastTupleElementDestructuring.ts
Normal file
11
tests/cases/compiler/lastTupleElementDestructuring.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// Test for fixing excess property checking when accessing last tuple element in destructuring
|
||||
declare function foo<T extends { dataType: 'a' | 'b' }>(template: T): [T, any, any];
|
||||
|
||||
// This should NOT error after fix
|
||||
const [, , last] = foo({ dataType: 'a', day: 0 });
|
||||
|
||||
// This already works (doesn't access last element)
|
||||
const [, mid, ] = foo({ dataType: 'a', day: 0 });
|
||||
|
||||
// Also test that legitimate errors are still caught
|
||||
const [, , last2] = foo({ dataType: 'c' }); // Should still error
|
||||
8
tests/debug_test.ts
Normal file
8
tests/debug_test.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// Test case to check the fix
|
||||
declare function foo<T extends { dataType: 'a' | 'b' }>(template: T): [T, any, any];
|
||||
|
||||
// Error case - accessing last element
|
||||
const [, , last] = foo({ dataType: 'a', day: 0 });
|
||||
|
||||
// Working case - not accessing last element
|
||||
const [, mid, ] = foo({ dataType: 'a', day: 0 });
|
||||
Loading…
x
Reference in New Issue
Block a user