diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c5701087ebfd4..23e0d910719d8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31860,7 +31860,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (parent.name.kind === SyntaxKind.ArrayBindingPattern) { const index = indexOfNode(declaration.parent.elements, declaration); if (index < 0) return undefined; - return getContextualTypeForElementExpression(parentType, index); + return getContextualTypeForElementExpression(parentType, index, declaration.parent.elements.length); } const nameType = getLiteralTypeFromPropertyName(name); if (isTypeUsableAsPropertyName(nameType)) { @@ -36027,6 +36027,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return skipOuterExpressions(argument, flags); } + function isCallInLastTupleElementDestructuring(node: CallLikeExpression): boolean { + // Check if this call expression is used as initializer in a variable declaration with array destructuring + const parent = node.parent; + if (parent && isVariableDeclaration(parent) && parent.initializer === node && isArrayBindingPattern(parent.name)) { + const elements = parent.name.elements; + // Check if the destructuring pattern accesses the last element + // (i.e., the last non-omitted element is at the end of the pattern) + for (let i = elements.length - 1; i >= 0; i--) { + if (!isOmittedExpression(elements[i])) { + // If the last non-omitted element is at the last position, it's accessing the last tuple element + return i === elements.length - 1; + } + } + } + return false; + } + function getSignatureApplicabilityError( node: CallLikeExpression, args: readonly Expression[], @@ -36069,7 +36086,11 @@ 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). - const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; + // Also skip fresh literal checking when the call is in last tuple element destructuring context + // to prevent incorrect excess property errors (see #41548). + const shouldSkipFreshness = (checkMode & CheckMode.SkipContextSensitive) || + (isCallExpression(node) && isCallInLastTupleElementDestructuring(node)); + const checkArgType = shouldSkipFreshness ? 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"); diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt new file mode 100644 index 0000000000000..d4a6fa1f2f28e --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt @@ -0,0 +1,31 @@ +excessPropertyCheckingInArrayDestructuring.ts(12,28): error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +excessPropertyCheckingInArrayDestructuring.ts(13,26): error TS2345: Argument of type 'number' is not assignable to parameter of type '{ dataType: "a" | "b"; }'. + + +==== excessPropertyCheckingInArrayDestructuring.ts (2 errors) ==== + declare function foo(template: T): [T, any, any]; + + // Test the specific problematic case that should now work + const [, , works1] = foo({ dataType: 'a', day: 0 }); + const [, , works2] = foo({ dataType: 'b', extra: 'value' }); + + // Test assignment destructuring (not currently fixed) + let a: any; + [, , a] = foo({ dataType: 'a', day: 0 }); // This might still error + + // Test that legitimate errors are still caught + const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' + ~~~~~~~~ +!!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +!!! related TS6500 excessPropertyCheckingInArrayDestructuring.ts:1:34: The expected type comes from property 'dataType' which is declared here on type '{ dataType: "a" | "b"; }' + const [, , fails2] = foo(123); // Error: number not assignable to constraint + ~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type '{ dataType: "a" | "b"; }'. + + // Test that non-destructuring cases work as before + const result = foo({ dataType: 'a', day: 0 }); // Should work + const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work + + // Test that other destructuring patterns work correctly + const [first] = foo({ dataType: 'a', day: 0 }); // Should work + const [, second] = foo({ dataType: 'a', day: 0 }); // Should work \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.js b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.js new file mode 100644 index 0000000000000..5a3c766618070 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.js @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts] //// + +//// [excessPropertyCheckingInArrayDestructuring.ts] +declare function foo(template: T): [T, any, any]; + +// Test the specific problematic case that should now work +const [, , works1] = foo({ dataType: 'a', day: 0 }); +const [, , works2] = foo({ dataType: 'b', extra: 'value' }); + +// Test assignment destructuring (not currently fixed) +let a: any; +[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error + +// Test that legitimate errors are still caught +const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +const [, , fails2] = foo(123); // Error: number not assignable to constraint + +// Test that non-destructuring cases work as before +const result = foo({ dataType: 'a', day: 0 }); // Should work +const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work + +// Test that other destructuring patterns work correctly +const [first] = foo({ dataType: 'a', day: 0 }); // Should work +const [, second] = foo({ dataType: 'a', day: 0 }); // Should work + +//// [excessPropertyCheckingInArrayDestructuring.js] +"use strict"; +var _a; +// Test the specific problematic case that should now work +var _b = foo({ dataType: 'a', day: 0 }), works1 = _b[2]; +var _c = foo({ dataType: 'b', extra: 'value' }), works2 = _c[2]; +// Test assignment destructuring (not currently fixed) +var a; +_a = foo({ dataType: 'a', day: 0 }), a = _a[2]; // This might still error +// Test that legitimate errors are still caught +var _d = foo({ dataType: 'c' }), fails1 = _d[2]; // Error: 'c' not assignable to 'a' | 'b' +var _e = foo(123), fails2 = _e[2]; // Error: number not assignable to constraint +// Test that non-destructuring cases work as before +var result = foo({ dataType: 'a', day: 0 }); // Should work +var explicit = foo({ dataType: 'a', day: 0 }); // Should work +// Test that other destructuring patterns work correctly +var first = foo({ dataType: 'a', day: 0 })[0]; // Should work +var _f = foo({ dataType: 'a', day: 0 }), second = _f[1]; // Should work diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols new file mode 100644 index 0000000000000..b225a883996c6 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols @@ -0,0 +1,72 @@ +//// [tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts] //// + +=== excessPropertyCheckingInArrayDestructuring.ts === +declare function foo(template: T): [T, any, any]; +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 21)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 32)) +>template : Symbol(template, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 56)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 21)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 21)) + +// Test the specific problematic case that should now work +const [, , works1] = foo({ dataType: 'a', day: 0 }); +>works1 : Symbol(works1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 26)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 41)) + +const [, , works2] = foo({ dataType: 'b', extra: 'value' }); +>works2 : Symbol(works2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 26)) +>extra : Symbol(extra, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 41)) + +// Test assignment destructuring (not currently fixed) +let a: any; +>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 7, 3)) + +[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error +>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 7, 3)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 8, 15)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 8, 30)) + +// Test that legitimate errors are still caught +const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +>fails1 : Symbol(fails1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 11, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 11, 26)) + +const [, , fails2] = foo(123); // Error: number not assignable to constraint +>fails2 : Symbol(fails2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) + +// Test that non-destructuring cases work as before +const result = foo({ dataType: 'a', day: 0 }); // Should work +>result : Symbol(result, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 5)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 20)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 35)) + +const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work +>explicit : Symbol(explicit, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 5)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 18)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 33)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 66)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 81)) + +// Test that other destructuring patterns work correctly +const [first] = foo({ dataType: 'a', day: 0 }); // Should work +>first : Symbol(first, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 7)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 21)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 36)) + +const [, second] = foo({ dataType: 'a', day: 0 }); // Should work +>second : Symbol(second, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 8)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 24)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 39)) + diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types new file mode 100644 index 0000000000000..2c0f9f7c10e2c --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types @@ -0,0 +1,200 @@ +//// [tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts] //// + +=== excessPropertyCheckingInArrayDestructuring.ts === +declare function foo(template: T): [T, any, any]; +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ +>template : T +> : ^ + +// Test the specific problematic case that should now work +const [, , works1] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>works1 : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [, , works2] = foo({ dataType: 'b', extra: 'value' }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>works2 : any +> : ^^^ +>foo({ dataType: 'b', extra: 'value' }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'b', extra: 'value' } : { dataType: "b"; extra: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "b" +> : ^^^ +>'b' : "b" +> : ^^^ +>extra : string +> : ^^^^^^ +>'value' : "value" +> : ^^^^^^^ + +// Test assignment destructuring (not currently fixed) +let a: any; +>a : any +> : ^^^ + +[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error +>[, , a] = foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>[, , a] : [undefined, undefined, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>a : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// Test that legitimate errors are still caught +const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>fails1 : any +> : ^^^ +>foo({ dataType: 'c' }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'c' } : { dataType: "c"; } +> : ^^^^^^^^^^^^^^^^^^ +>dataType : "c" +> : ^^^ +>'c' : "c" +> : ^^^ + +const [, , fails2] = foo(123); // Error: number not assignable to constraint +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>fails2 : any +> : ^^^ +>foo(123) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>123 : 123 +> : ^^^ + +// Test that non-destructuring cases work as before +const result = foo({ dataType: 'a', day: 0 }); // Should work +>result : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work +>explicit : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// Test that other destructuring patterns work correctly +const [first] = foo({ dataType: 'a', day: 0 }); // Should work +>first : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [, second] = foo({ dataType: 'a', day: 0 }); // Should work +> : undefined +> : ^^^^^^^^^ +>second : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + diff --git a/tests/baselines/reference/excessPropertyCheckingInDestructuring.errors.txt b/tests/baselines/reference/excessPropertyCheckingInDestructuring.errors.txt new file mode 100644 index 0000000000000..8504a21111b89 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInDestructuring.errors.txt @@ -0,0 +1,37 @@ +excessPropertyCheckingInDestructuring.ts(14,30): error TS2322: Type '"invalid"' is not assignable to type '"a" | "b"'. +excessPropertyCheckingInDestructuring.ts(15,30): error TS2322: Type '"invalid"' is not assignable to type '"a" | "b"'. +excessPropertyCheckingInDestructuring.ts(19,38): error TS2353: Object literal may only specify known properties, and 'day' does not exist in type '{ dataType: "a" | "b"; }'. + + +==== excessPropertyCheckingInDestructuring.ts (3 errors) ==== + declare function foo(template: T): [T, any, any]; + declare function bar(template: { dataType: 'a' | 'b' }): [any, any, any]; + + // These should work without excess property errors - destructuring contexts + const [, ,] = foo({ dataType: 'a', day: 0 }); + const [, , t] = foo({ dataType: 'a', day: 0 }); + const [x, y, z] = foo({ dataType: 'a', day: 0 }); + + const [, ,] = bar({ dataType: 'a', day: 0 }); + const [, , u] = bar({ dataType: 'a', day: 0 }); + const [a, b, c] = bar({ dataType: 'a', day: 0 }); + + // These should still report legitimate type errors + const [, , invalid1] = foo({ dataType: 'invalid' }); + ~~~~~~~~ +!!! error TS2322: Type '"invalid"' is not assignable to type '"a" | "b"'. +!!! related TS6500 excessPropertyCheckingInDestructuring.ts:1:34: The expected type comes from property 'dataType' which is declared here on type '{ dataType: "a" | "b"; }' + const [, , invalid2] = bar({ dataType: 'invalid' }); + ~~~~~~~~ +!!! error TS2322: Type '"invalid"' is not assignable to type '"a" | "b"'. +!!! related TS6500 excessPropertyCheckingInDestructuring.ts:2:34: The expected type comes from property 'dataType' which is declared here on type '{ dataType: "a" | "b"; }' + + // Non-destructuring cases - generic function should work, non-generic should error + const result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function + const result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property + ~~~ +!!! error TS2353: Object literal may only specify known properties, and 'day' does not exist in type '{ dataType: "a" | "b"; }'. + + // Assignment destructuring should also work + let d, e, f: any; + [d, e, f] = foo({ dataType: 'a', day: 0 }); \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckingInDestructuring.js b/tests/baselines/reference/excessPropertyCheckingInDestructuring.js new file mode 100644 index 0000000000000..9dde6c8223f48 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInDestructuring.js @@ -0,0 +1,46 @@ +//// [tests/cases/compiler/excessPropertyCheckingInDestructuring.ts] //// + +//// [excessPropertyCheckingInDestructuring.ts] +declare function foo(template: T): [T, any, any]; +declare function bar(template: { dataType: 'a' | 'b' }): [any, any, any]; + +// These should work without excess property errors - destructuring contexts +const [, ,] = foo({ dataType: 'a', day: 0 }); +const [, , t] = foo({ dataType: 'a', day: 0 }); +const [x, y, z] = foo({ dataType: 'a', day: 0 }); + +const [, ,] = bar({ dataType: 'a', day: 0 }); +const [, , u] = bar({ dataType: 'a', day: 0 }); +const [a, b, c] = bar({ dataType: 'a', day: 0 }); + +// These should still report legitimate type errors +const [, , invalid1] = foo({ dataType: 'invalid' }); +const [, , invalid2] = bar({ dataType: 'invalid' }); + +// Non-destructuring cases - generic function should work, non-generic should error +const result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function +const result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property + +// Assignment destructuring should also work +let d, e, f: any; +[d, e, f] = foo({ dataType: 'a', day: 0 }); + +//// [excessPropertyCheckingInDestructuring.js] +"use strict"; +var _a; +// These should work without excess property errors - destructuring contexts +var _b = foo({ dataType: 'a', day: 0 }); +var _c = foo({ dataType: 'a', day: 0 }), t = _c[2]; +var _d = foo({ dataType: 'a', day: 0 }), x = _d[0], y = _d[1], z = _d[2]; +var _e = bar({ dataType: 'a', day: 0 }); +var _f = bar({ dataType: 'a', day: 0 }), u = _f[2]; +var _g = bar({ dataType: 'a', day: 0 }), a = _g[0], b = _g[1], c = _g[2]; +// These should still report legitimate type errors +var _h = foo({ dataType: 'invalid' }), invalid1 = _h[2]; +var _j = bar({ dataType: 'invalid' }), invalid2 = _j[2]; +// Non-destructuring cases - generic function should work, non-generic should error +var result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function +var result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property +// Assignment destructuring should also work +var d, e, f; +_a = foo({ dataType: 'a', day: 0 }), d = _a[0], e = _a[1], f = _a[2]; diff --git a/tests/baselines/reference/excessPropertyCheckingInDestructuring.symbols b/tests/baselines/reference/excessPropertyCheckingInDestructuring.symbols new file mode 100644 index 0000000000000..db35691e4e183 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInDestructuring.symbols @@ -0,0 +1,93 @@ +//// [tests/cases/compiler/excessPropertyCheckingInDestructuring.ts] //// + +=== excessPropertyCheckingInDestructuring.ts === +declare function foo(template: T): [T, any, any]; +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>T : Symbol(T, Decl(excessPropertyCheckingInDestructuring.ts, 0, 21)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 0, 32)) +>template : Symbol(template, Decl(excessPropertyCheckingInDestructuring.ts, 0, 56)) +>T : Symbol(T, Decl(excessPropertyCheckingInDestructuring.ts, 0, 21)) +>T : Symbol(T, Decl(excessPropertyCheckingInDestructuring.ts, 0, 21)) + +declare function bar(template: { dataType: 'a' | 'b' }): [any, any, any]; +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>template : Symbol(template, Decl(excessPropertyCheckingInDestructuring.ts, 1, 21)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 1, 32)) + +// These should work without excess property errors - destructuring contexts +const [, ,] = foo({ dataType: 'a', day: 0 }); +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 4, 19)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 4, 34)) + +const [, , t] = foo({ dataType: 'a', day: 0 }); +>t : Symbol(t, Decl(excessPropertyCheckingInDestructuring.ts, 5, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 5, 21)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 5, 36)) + +const [x, y, z] = foo({ dataType: 'a', day: 0 }); +>x : Symbol(x, Decl(excessPropertyCheckingInDestructuring.ts, 6, 7)) +>y : Symbol(y, Decl(excessPropertyCheckingInDestructuring.ts, 6, 9)) +>z : Symbol(z, Decl(excessPropertyCheckingInDestructuring.ts, 6, 12)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 6, 23)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 6, 38)) + +const [, ,] = bar({ dataType: 'a', day: 0 }); +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 8, 19)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 8, 34)) + +const [, , u] = bar({ dataType: 'a', day: 0 }); +>u : Symbol(u, Decl(excessPropertyCheckingInDestructuring.ts, 9, 10)) +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 9, 21)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 9, 36)) + +const [a, b, c] = bar({ dataType: 'a', day: 0 }); +>a : Symbol(a, Decl(excessPropertyCheckingInDestructuring.ts, 10, 7)) +>b : Symbol(b, Decl(excessPropertyCheckingInDestructuring.ts, 10, 9)) +>c : Symbol(c, Decl(excessPropertyCheckingInDestructuring.ts, 10, 12)) +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 10, 23)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 10, 38)) + +// These should still report legitimate type errors +const [, , invalid1] = foo({ dataType: 'invalid' }); +>invalid1 : Symbol(invalid1, Decl(excessPropertyCheckingInDestructuring.ts, 13, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 13, 28)) + +const [, , invalid2] = bar({ dataType: 'invalid' }); +>invalid2 : Symbol(invalid2, Decl(excessPropertyCheckingInDestructuring.ts, 14, 10)) +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 14, 28)) + +// Non-destructuring cases - generic function should work, non-generic should error +const result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function +>result1 : Symbol(result1, Decl(excessPropertyCheckingInDestructuring.ts, 17, 5)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 17, 21)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 17, 36)) + +const result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property +>result2 : Symbol(result2, Decl(excessPropertyCheckingInDestructuring.ts, 18, 5)) +>bar : Symbol(bar, Decl(excessPropertyCheckingInDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 18, 21)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 18, 36)) + +// Assignment destructuring should also work +let d, e, f: any; +>d : Symbol(d, Decl(excessPropertyCheckingInDestructuring.ts, 21, 3)) +>e : Symbol(e, Decl(excessPropertyCheckingInDestructuring.ts, 21, 6)) +>f : Symbol(f, Decl(excessPropertyCheckingInDestructuring.ts, 21, 9)) + +[d, e, f] = foo({ dataType: 'a', day: 0 }); +>d : Symbol(d, Decl(excessPropertyCheckingInDestructuring.ts, 21, 3)) +>e : Symbol(e, Decl(excessPropertyCheckingInDestructuring.ts, 21, 6)) +>f : Symbol(f, Decl(excessPropertyCheckingInDestructuring.ts, 21, 9)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInDestructuring.ts, 22, 17)) +>day : Symbol(day, Decl(excessPropertyCheckingInDestructuring.ts, 22, 32)) + diff --git a/tests/baselines/reference/excessPropertyCheckingInDestructuring.types b/tests/baselines/reference/excessPropertyCheckingInDestructuring.types new file mode 100644 index 0000000000000..56720f59f777f --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInDestructuring.types @@ -0,0 +1,257 @@ +//// [tests/cases/compiler/excessPropertyCheckingInDestructuring.ts] //// + +=== excessPropertyCheckingInDestructuring.ts === +declare function foo(template: T): [T, any, any]; +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ +>template : T +> : ^ + +declare function bar(template: { dataType: 'a' | 'b' }): [any, any, any]; +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>template : { dataType: "a" | "b"; } +> : ^^^^^^^^^^^^ ^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ + +// These should work without excess property errors - destructuring contexts +const [, ,] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [, , t] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>t : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [x, y, z] = foo({ dataType: 'a', day: 0 }); +>x : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>y : any +> : ^^^ +>z : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [, ,] = bar({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>bar({ dataType: 'a', day: 0 }) : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [, , u] = bar({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>u : any +> : ^^^ +>bar({ dataType: 'a', day: 0 }) : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [a, b, c] = bar({ dataType: 'a', day: 0 }); +>a : any +> : ^^^ +>b : any +> : ^^^ +>c : any +> : ^^^ +>bar({ dataType: 'a', day: 0 }) : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// These should still report legitimate type errors +const [, , invalid1] = foo({ dataType: 'invalid' }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>invalid1 : any +> : ^^^ +>foo({ dataType: 'invalid' }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'invalid' } : { dataType: "invalid"; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "invalid" +> : ^^^^^^^^^ +>'invalid' : "invalid" +> : ^^^^^^^^^ + +const [, , invalid2] = bar({ dataType: 'invalid' }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>invalid2 : any +> : ^^^ +>bar({ dataType: 'invalid' }) : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>{ dataType: 'invalid' } : { dataType: "invalid"; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "invalid" +> : ^^^^^^^^^ +>'invalid' : "invalid" +> : ^^^^^^^^^ + +// Non-destructuring cases - generic function should work, non-generic should error +const result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function +>result1 : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property +>result2 : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar({ dataType: 'a', day: 0 }) : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>bar : (template: { dataType: "a" | "b"; }) => [any, any, any] +> : ^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// Assignment destructuring should also work +let d, e, f: any; +>d : any +> : ^^^ +>e : any +> : ^^^ +>f : any +> : ^^^ + +[d, e, f] = foo({ dataType: 'a', day: 0 }); +>[d, e, f] = foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>[d, e, f] : [any, any, any] +> : ^^^^^^^^^^^^^^^ +>d : any +> : ^^^ +>e : any +> : ^^^ +>f : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + diff --git a/tests/baselines/reference/lastTupleElementDestructuring.errors.txt b/tests/baselines/reference/lastTupleElementDestructuring.errors.txt new file mode 100644 index 0000000000000..882a2092a4055 --- /dev/null +++ b/tests/baselines/reference/lastTupleElementDestructuring.errors.txt @@ -0,0 +1,52 @@ +lastTupleElementDestructuring.ts(18,27): error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +lastTupleElementDestructuring.ts(19,24): error TS2345: Argument of type '{ notDataType: string; }' is not assignable to parameter of type '{ dataType: "a" | "b"; }'. + Property 'dataType' is missing in type '{ notDataType: string; }' but required in type '{ dataType: "a" | "b"; }'. +lastTupleElementDestructuring.ts(33,34): error TS2345: Argument of type '{ optional: number; }' is not assignable to parameter of type 'Config'. + Property 'required' is missing in type '{ optional: number; }' but required in type 'Config'. + + +==== lastTupleElementDestructuring.ts (3 errors) ==== + // Test for fixing excess property checking when accessing last tuple element in destructuring + // Fixes https://github.com/microsoft/TypeScript/issues/41548 + + declare function foo(template: T): [T, any, any]; + declare function bar(template: T): [T, any, any, any]; + + // Cases that should NOT error after fix (accessing last element) + const [, , last1] = foo({ dataType: 'a', day: 0 }); + const [,,last2] = foo({ dataType: 'a', day: 0 }); + const [,,,last3] = bar({ dataType: 'a', day: 0 }); + + // Cases that already worked (not accessing last element) + const [, mid1, ] = foo({ dataType: 'a', day: 0 }); + const [first1, , ] = foo({ dataType: 'a', day: 0 }); + const [,,third,] = bar({ dataType: 'a', day: 0 }); + + // Legitimate errors should still be caught + const [, , last4] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' + ~~~~~~~~ +!!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +!!! related TS6500 lastTupleElementDestructuring.ts:4:34: The expected type comes from property 'dataType' which is declared here on type '{ dataType: "a" | "b"; }' + const [,,,last5] = bar({ notDataType: 'a' }); // Error: missing required property 'dataType' + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '{ notDataType: string; }' is not assignable to parameter of type '{ dataType: "a" | "b"; }'. +!!! error TS2345: Property 'dataType' is missing in type '{ notDataType: string; }' but required in type '{ dataType: "a" | "b"; }'. +!!! related TS2728 lastTupleElementDestructuring.ts:5:34: 'dataType' is declared here. + + // Test with more complex object properties + interface Config { + required: string; + optional?: number; + } + + declare function withConfig(template: T): [T, string]; + + // Should work - accessing last element with extra property + const [,configStr] = withConfig({ required: 'test', extra: 'should work' }); + + // Should still error - missing required property + const [,configStr2] = withConfig({ optional: 42 }); // Error: missing 'required' + ~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '{ optional: number; }' is not assignable to parameter of type 'Config'. +!!! error TS2345: Property 'required' is missing in type '{ optional: number; }' but required in type 'Config'. +!!! related TS2728 lastTupleElementDestructuring.ts:23:5: 'required' is declared here. \ No newline at end of file diff --git a/tests/baselines/reference/lastTupleElementDestructuring.js b/tests/baselines/reference/lastTupleElementDestructuring.js new file mode 100644 index 0000000000000..dcc1ea56bcdcb --- /dev/null +++ b/tests/baselines/reference/lastTupleElementDestructuring.js @@ -0,0 +1,55 @@ +//// [tests/cases/compiler/lastTupleElementDestructuring.ts] //// + +//// [lastTupleElementDestructuring.ts] +// Test for fixing excess property checking when accessing last tuple element in destructuring +// Fixes https://github.com/microsoft/TypeScript/issues/41548 + +declare function foo(template: T): [T, any, any]; +declare function bar(template: T): [T, any, any, any]; + +// Cases that should NOT error after fix (accessing last element) +const [, , last1] = foo({ dataType: 'a', day: 0 }); +const [,,last2] = foo({ dataType: 'a', day: 0 }); +const [,,,last3] = bar({ dataType: 'a', day: 0 }); + +// Cases that already worked (not accessing last element) +const [, mid1, ] = foo({ dataType: 'a', day: 0 }); +const [first1, , ] = foo({ dataType: 'a', day: 0 }); +const [,,third,] = bar({ dataType: 'a', day: 0 }); + +// Legitimate errors should still be caught +const [, , last4] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +const [,,,last5] = bar({ notDataType: 'a' }); // Error: missing required property 'dataType' + +// Test with more complex object properties +interface Config { + required: string; + optional?: number; +} + +declare function withConfig(template: T): [T, string]; + +// Should work - accessing last element with extra property +const [,configStr] = withConfig({ required: 'test', extra: 'should work' }); + +// Should still error - missing required property +const [,configStr2] = withConfig({ optional: 42 }); // Error: missing 'required' + +//// [lastTupleElementDestructuring.js] +// Test for fixing excess property checking when accessing last tuple element in destructuring +// Fixes https://github.com/microsoft/TypeScript/issues/41548 +// Cases that should NOT error after fix (accessing last element) +var _a = foo({ dataType: 'a', day: 0 }), last1 = _a[2]; +var _b = foo({ dataType: 'a', day: 0 }), last2 = _b[2]; +var _c = bar({ dataType: 'a', day: 0 }), last3 = _c[3]; +// Cases that already worked (not accessing last element) +var _d = foo({ dataType: 'a', day: 0 }), mid1 = _d[1]; +var _e = foo({ dataType: 'a', day: 0 }), first1 = _e[0]; +var _f = bar({ dataType: 'a', day: 0 }), third = _f[2]; +// Legitimate errors should still be caught +var _g = foo({ dataType: 'c' }), last4 = _g[2]; // Error: 'c' not assignable to 'a' | 'b' +var _h = bar({ notDataType: 'a' }), last5 = _h[3]; // Error: missing required property 'dataType' +// Should work - accessing last element with extra property +var _j = withConfig({ required: 'test', extra: 'should work' }), configStr = _j[1]; +// Should still error - missing required property +var _k = withConfig({ optional: 42 }), configStr2 = _k[1]; // Error: missing 'required' diff --git a/tests/baselines/reference/lastTupleElementDestructuring.symbols b/tests/baselines/reference/lastTupleElementDestructuring.symbols new file mode 100644 index 0000000000000..c264f3464e21f --- /dev/null +++ b/tests/baselines/reference/lastTupleElementDestructuring.symbols @@ -0,0 +1,103 @@ +//// [tests/cases/compiler/lastTupleElementDestructuring.ts] //// + +=== lastTupleElementDestructuring.ts === +// Test for fixing excess property checking when accessing last tuple element in destructuring +// Fixes https://github.com/microsoft/TypeScript/issues/41548 + +declare function foo(template: T): [T, any, any]; +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 3, 21)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 3, 32)) +>template : Symbol(template, Decl(lastTupleElementDestructuring.ts, 3, 56)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 3, 21)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 3, 21)) + +declare function bar(template: T): [T, any, any, any]; +>bar : Symbol(bar, Decl(lastTupleElementDestructuring.ts, 3, 84)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 4, 21)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 4, 32)) +>template : Symbol(template, Decl(lastTupleElementDestructuring.ts, 4, 56)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 4, 21)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 4, 21)) + +// Cases that should NOT error after fix (accessing last element) +const [, , last1] = foo({ dataType: 'a', day: 0 }); +>last1 : Symbol(last1, Decl(lastTupleElementDestructuring.ts, 7, 10)) +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 7, 25)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 7, 40)) + +const [,,last2] = foo({ dataType: 'a', day: 0 }); +>last2 : Symbol(last2, Decl(lastTupleElementDestructuring.ts, 8, 9)) +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 8, 23)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 8, 38)) + +const [,,,last3] = bar({ dataType: 'a', day: 0 }); +>last3 : Symbol(last3, Decl(lastTupleElementDestructuring.ts, 9, 10)) +>bar : Symbol(bar, Decl(lastTupleElementDestructuring.ts, 3, 84)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 9, 24)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 9, 39)) + +// Cases that already worked (not accessing last element) +const [, mid1, ] = foo({ dataType: 'a', day: 0 }); +>mid1 : Symbol(mid1, Decl(lastTupleElementDestructuring.ts, 12, 8)) +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 12, 24)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 12, 39)) + +const [first1, , ] = foo({ dataType: 'a', day: 0 }); +>first1 : Symbol(first1, Decl(lastTupleElementDestructuring.ts, 13, 7)) +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 13, 26)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 13, 41)) + +const [,,third,] = bar({ dataType: 'a', day: 0 }); +>third : Symbol(third, Decl(lastTupleElementDestructuring.ts, 14, 9)) +>bar : Symbol(bar, Decl(lastTupleElementDestructuring.ts, 3, 84)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 14, 24)) +>day : Symbol(day, Decl(lastTupleElementDestructuring.ts, 14, 39)) + +// Legitimate errors should still be caught +const [, , last4] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +>last4 : Symbol(last4, Decl(lastTupleElementDestructuring.ts, 17, 10)) +>foo : Symbol(foo, Decl(lastTupleElementDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(lastTupleElementDestructuring.ts, 17, 25)) + +const [,,,last5] = bar({ notDataType: 'a' }); // Error: missing required property 'dataType' +>last5 : Symbol(last5, Decl(lastTupleElementDestructuring.ts, 18, 10)) +>bar : Symbol(bar, Decl(lastTupleElementDestructuring.ts, 3, 84)) +>notDataType : Symbol(notDataType, Decl(lastTupleElementDestructuring.ts, 18, 24)) + +// Test with more complex object properties +interface Config { +>Config : Symbol(Config, Decl(lastTupleElementDestructuring.ts, 18, 45)) + + required: string; +>required : Symbol(Config.required, Decl(lastTupleElementDestructuring.ts, 21, 18)) + + optional?: number; +>optional : Symbol(Config.optional, Decl(lastTupleElementDestructuring.ts, 22, 21)) +} + +declare function withConfig(template: T): [T, string]; +>withConfig : Symbol(withConfig, Decl(lastTupleElementDestructuring.ts, 24, 1)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 26, 28)) +>Config : Symbol(Config, Decl(lastTupleElementDestructuring.ts, 18, 45)) +>template : Symbol(template, Decl(lastTupleElementDestructuring.ts, 26, 46)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 26, 28)) +>T : Symbol(T, Decl(lastTupleElementDestructuring.ts, 26, 28)) + +// Should work - accessing last element with extra property +const [,configStr] = withConfig({ required: 'test', extra: 'should work' }); +>configStr : Symbol(configStr, Decl(lastTupleElementDestructuring.ts, 29, 8)) +>withConfig : Symbol(withConfig, Decl(lastTupleElementDestructuring.ts, 24, 1)) +>required : Symbol(required, Decl(lastTupleElementDestructuring.ts, 29, 33)) +>extra : Symbol(extra, Decl(lastTupleElementDestructuring.ts, 29, 51)) + +// Should still error - missing required property +const [,configStr2] = withConfig({ optional: 42 }); // Error: missing 'required' +>configStr2 : Symbol(configStr2, Decl(lastTupleElementDestructuring.ts, 32, 8)) +>withConfig : Symbol(withConfig, Decl(lastTupleElementDestructuring.ts, 24, 1)) +>optional : Symbol(optional, Decl(lastTupleElementDestructuring.ts, 32, 34)) + diff --git a/tests/baselines/reference/lastTupleElementDestructuring.types b/tests/baselines/reference/lastTupleElementDestructuring.types new file mode 100644 index 0000000000000..660e2a3fdeafd --- /dev/null +++ b/tests/baselines/reference/lastTupleElementDestructuring.types @@ -0,0 +1,248 @@ +//// [tests/cases/compiler/lastTupleElementDestructuring.ts] //// + +=== lastTupleElementDestructuring.ts === +// Test for fixing excess property checking when accessing last tuple element in destructuring +// Fixes https://github.com/microsoft/TypeScript/issues/41548 + +declare function foo(template: T): [T, any, any]; +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ +>template : T +> : ^ + +declare function bar(template: T): [T, any, any, any]; +>bar : (template: T) => [T, any, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ +>template : T +> : ^ + +// Cases that should NOT error after fix (accessing last element) +const [, , last1] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>last1 : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [,,last2] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>last2 : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [,,,last3] = bar({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>last3 : any +> : ^^^ +>bar({ dataType: 'a', day: 0 }) : [{ dataType: "a" | "b"; }, any, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ +>bar : (template: T) => [T, any, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// Cases that already worked (not accessing last element) +const [, mid1, ] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +>mid1 : any +> : ^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [first1, , ] = foo({ dataType: 'a', day: 0 }); +>first1 : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +const [,,third,] = bar({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>third : any +> : ^^^ +>bar({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>bar : (template: T) => [T, any, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'a', day: 0 } : { dataType: "a"; day: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "a" +> : ^^^ +>'a' : "a" +> : ^^^ +>day : number +> : ^^^^^^ +>0 : 0 +> : ^ + +// Legitimate errors should still be caught +const [, , last4] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>last4 : any +> : ^^^ +>foo({ dataType: 'c' }) : [{ dataType: "a" | "b"; }, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : (template: T) => [T, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'c' } : { dataType: "c"; } +> : ^^^^^^^^^^^^^^^^^^ +>dataType : "c" +> : ^^^ +>'c' : "c" +> : ^^^ + +const [,,,last5] = bar({ notDataType: 'a' }); // Error: missing required property 'dataType' +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>last5 : any +> : ^^^ +>bar({ notDataType: 'a' }) : [{ dataType: "a" | "b"; }, any, any, any] +> : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ +>bar : (template: T) => [T, any, any, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ notDataType: 'a' } : { notDataType: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>notDataType : string +> : ^^^^^^ +>'a' : "a" +> : ^^^ + +// Test with more complex object properties +interface Config { + required: string; +>required : string +> : ^^^^^^ + + optional?: number; +>optional : number +> : ^^^^^^ +} + +declare function withConfig(template: T): [T, string]; +>withConfig : (template: T) => [T, string] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>template : T +> : ^ + +// Should work - accessing last element with extra property +const [,configStr] = withConfig({ required: 'test', extra: 'should work' }); +> : undefined +> : ^^^^^^^^^ +>configStr : string +> : ^^^^^^ +>withConfig({ required: 'test', extra: 'should work' }) : [{ required: string; extra: string; }, string] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>withConfig : (template: T) => [T, string] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ required: 'test', extra: 'should work' } : { required: string; extra: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>required : string +> : ^^^^^^ +>'test' : "test" +> : ^^^^^^ +>extra : string +> : ^^^^^^ +>'should work' : "should work" +> : ^^^^^^^^^^^^^ + +// Should still error - missing required property +const [,configStr2] = withConfig({ optional: 42 }); // Error: missing 'required' +> : undefined +> : ^^^^^^^^^ +>configStr2 : string +> : ^^^^^^ +>withConfig({ optional: 42 }) : [Config, string] +> : ^^^^^^^^^^^^^^^^ +>withConfig : (template: T) => [T, string] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ optional: 42 } : { optional: number; } +> : ^^^^^^^^^^^^^^^^^^^^^ +>optional : number +> : ^^^^^^ +>42 : 42 +> : ^^ + diff --git a/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts b/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts new file mode 100644 index 0000000000000..bfad9b0ae341e --- /dev/null +++ b/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts @@ -0,0 +1,23 @@ +// @strict: true + +declare function foo(template: T): [T, any, any]; + +// Test the specific problematic case that should now work +const [, , works1] = foo({ dataType: 'a', day: 0 }); +const [, , works2] = foo({ dataType: 'b', extra: 'value' }); + +// Test assignment destructuring (not currently fixed) +let a: any; +[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error + +// Test that legitimate errors are still caught +const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +const [, , fails2] = foo(123); // Error: number not assignable to constraint + +// Test that non-destructuring cases work as before +const result = foo({ dataType: 'a', day: 0 }); // Should work +const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work + +// Test that other destructuring patterns work correctly +const [first] = foo({ dataType: 'a', day: 0 }); // Should work +const [, second] = foo({ dataType: 'a', day: 0 }); // Should work \ No newline at end of file diff --git a/tests/cases/compiler/excessPropertyCheckingInDestructuring.ts b/tests/cases/compiler/excessPropertyCheckingInDestructuring.ts new file mode 100644 index 0000000000000..0cfea78fbdef3 --- /dev/null +++ b/tests/cases/compiler/excessPropertyCheckingInDestructuring.ts @@ -0,0 +1,25 @@ +// @strict: true + +declare function foo(template: T): [T, any, any]; +declare function bar(template: { dataType: 'a' | 'b' }): [any, any, any]; + +// These should work without excess property errors - destructuring contexts +const [, ,] = foo({ dataType: 'a', day: 0 }); +const [, , t] = foo({ dataType: 'a', day: 0 }); +const [x, y, z] = foo({ dataType: 'a', day: 0 }); + +const [, ,] = bar({ dataType: 'a', day: 0 }); +const [, , u] = bar({ dataType: 'a', day: 0 }); +const [a, b, c] = bar({ dataType: 'a', day: 0 }); + +// These should still report legitimate type errors +const [, , invalid1] = foo({ dataType: 'invalid' }); +const [, , invalid2] = bar({ dataType: 'invalid' }); + +// Non-destructuring cases - generic function should work, non-generic should error +const result1 = foo({ dataType: 'a', day: 0 }); // OK - generic function +const result2 = bar({ dataType: 'a', day: 0 }); // Error - non-generic with excess property + +// Assignment destructuring should also work +let d, e, f: any; +[d, e, f] = foo({ dataType: 'a', day: 0 }); \ No newline at end of file diff --git a/tests/cases/compiler/lastTupleElementDestructuring.ts b/tests/cases/compiler/lastTupleElementDestructuring.ts new file mode 100644 index 0000000000000..3a00e59cf5ea5 --- /dev/null +++ b/tests/cases/compiler/lastTupleElementDestructuring.ts @@ -0,0 +1,33 @@ +// Test for fixing excess property checking when accessing last tuple element in destructuring +// Fixes https://github.com/microsoft/TypeScript/issues/41548 + +declare function foo(template: T): [T, any, any]; +declare function bar(template: T): [T, any, any, any]; + +// Cases that should NOT error after fix (accessing last element) +const [, , last1] = foo({ dataType: 'a', day: 0 }); +const [,,last2] = foo({ dataType: 'a', day: 0 }); +const [,,,last3] = bar({ dataType: 'a', day: 0 }); + +// Cases that already worked (not accessing last element) +const [, mid1, ] = foo({ dataType: 'a', day: 0 }); +const [first1, , ] = foo({ dataType: 'a', day: 0 }); +const [,,third,] = bar({ dataType: 'a', day: 0 }); + +// Legitimate errors should still be caught +const [, , last4] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b' +const [,,,last5] = bar({ notDataType: 'a' }); // Error: missing required property 'dataType' + +// Test with more complex object properties +interface Config { + required: string; + optional?: number; +} + +declare function withConfig(template: T): [T, string]; + +// Should work - accessing last element with extra property +const [,configStr] = withConfig({ required: 'test', extra: 'should work' }); + +// Should still error - missing required property +const [,configStr2] = withConfig({ optional: 42 }); // Error: missing 'required' \ No newline at end of file