diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c5701087ebf..c9c07be25a2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36062,14 +36062,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const restType = getNonArrayRestType(signature); const argCount = restType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length; for (let i = 0; i < argCount; i++) { - const arg = args[i]; - if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, /*inferenceContext*/ undefined, checkMode); - // 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; + const arg = args[i]; + if (arg.kind !== SyntaxKind.OmittedExpression) { + const paramType = getTypeAtPosition(signature, i); + const argType = checkExpressionWithContextualType(arg, paramType, /*inferenceContext*/ undefined, checkMode); + // 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 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"); @@ -36414,7 +36418,42 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { chain = chainDiagnosticMessages(chain, headMessage); return createDiagnosticForNodeArrayFromMessageChain(getSourceFileOfNode(node), typeArguments, chain); } - return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount); + 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)) { + // Check if we're destructuring at a position that causes inference issues + // Based on investigation, positions like 2, 4, 7, etc. can cause problems + const elements = parent.name.elements; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + if (!isOmittedExpression(element) && i >= 2) { + // Position 2 and higher can trigger the issue + return true; + } + } + } + } + + // Check for assignment expressions: [a, b, c] = foo() + if (parent && isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken && parent.right === node) { + if (isArrayLiteralExpression(parent.left)) { + // Similar check for assignment destructuring + const elements = parent.left.elements; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + if (!isOmittedExpression(element) && i >= 2) { + return true; + } + } + } + } + + return false; } function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, headMessage?: DiagnosticMessage): Signature { diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt new file mode 100644 index 00000000000..a6ad65975d4 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.errors.txt @@ -0,0 +1,36 @@ +excessPropertyCheckingInArrayDestructuring.ts(7,14): error TS2493: Tuple type '[{ dataType: "a"; day: number; }, any, any]' of length '3' has no element at index '3'. +excessPropertyCheckingInArrayDestructuring.ts(18,28): error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +excessPropertyCheckingInArrayDestructuring.ts(19,26): error TS2345: Argument of type 'number' is not assignable to parameter of type '{ dataType: "a" | "b"; }'. + + +==== excessPropertyCheckingInArrayDestructuring.ts (3 errors) ==== + declare function foo(template: T): [T, any, any]; + declare function bar(template: T): [any, T, any]; + + // Test cases that should work (no excess property errors) + const [, works1] = foo({ dataType: 'a', day: 0 }); + const [, , works2] = foo({ dataType: 'a', day: 0 }); + const [, , , works3] = foo({ dataType: 'a', day: 0 }); + ~~~~~~ +!!! error TS2493: Tuple type '[{ dataType: "a"; day: number; }, any, any]' of length '3' has no element at index '3'. + + // Test with different function signatures + const [, , works4] = bar({ dataType: 'b', extra: 'value' }); + + // Test assignment destructuring + let a: any, b: any, c: any; + [, , a] = foo({ dataType: 'a', day: 0 }); + [, b, ] = foo({ dataType: 'a', day: 0 }); + + // 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 \ 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 00000000000..4400c0b20ec --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.js @@ -0,0 +1,46 @@ +//// [tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts] //// + +//// [excessPropertyCheckingInArrayDestructuring.ts] +declare function foo(template: T): [T, any, any]; +declare function bar(template: T): [any, T, any]; + +// Test cases that should work (no excess property errors) +const [, works1] = foo({ dataType: 'a', day: 0 }); +const [, , works2] = foo({ dataType: 'a', day: 0 }); +const [, , , works3] = foo({ dataType: 'a', day: 0 }); + +// Test with different function signatures +const [, , works4] = bar({ dataType: 'b', extra: 'value' }); + +// Test assignment destructuring +let a: any, b: any, c: any; +[, , a] = foo({ dataType: 'a', day: 0 }); +[, b, ] = foo({ dataType: 'a', day: 0 }); + +// 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 + +//// [excessPropertyCheckingInArrayDestructuring.js] +"use strict"; +var _a, _b; +// Test cases that should work (no excess property errors) +var _c = foo({ dataType: 'a', day: 0 }), works1 = _c[1]; +var _d = foo({ dataType: 'a', day: 0 }), works2 = _d[2]; +var _e = foo({ dataType: 'a', day: 0 }), works3 = _e[3]; +// Test with different function signatures +var _f = bar({ dataType: 'b', extra: 'value' }), works4 = _f[2]; +// Test assignment destructuring +var a, b, c; +_a = foo({ dataType: 'a', day: 0 }), a = _a[2]; +_b = foo({ dataType: 'a', day: 0 }), b = _b[1]; +// Test that legitimate errors are still caught +var _g = foo({ dataType: 'c' }), fails1 = _g[2]; // Error: 'c' not assignable to 'a' | 'b' +var _h = foo(123), fails2 = _h[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 diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols new file mode 100644 index 00000000000..12e03b4d3de --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols @@ -0,0 +1,88 @@ +//// [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)) + +declare function bar(template: T): [any, T, any]; +>bar : Symbol(bar, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 84)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 32)) +>template : Symbol(template, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 56)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21)) +>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21)) + +// Test cases that should work (no excess property errors) +const [, works1] = foo({ dataType: 'a', day: 0 }); +>works1 : Symbol(works1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 8)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 24)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 39)) + +const [, , works2] = foo({ dataType: 'a', day: 0 }); +>works2 : Symbol(works2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 26)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 41)) + +const [, , , works3] = foo({ dataType: 'a', day: 0 }); +>works3 : Symbol(works3, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 12)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 28)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 43)) + +// Test with different function signatures +const [, , works4] = bar({ dataType: 'b', extra: 'value' }); +>works4 : Symbol(works4, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 10)) +>bar : Symbol(bar, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 84)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 26)) +>extra : Symbol(extra, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 41)) + +// Test assignment destructuring +let a: any, b: any, c: any; +>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 3)) +>b : Symbol(b, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 11)) +>c : Symbol(c, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 19)) + +[, , a] = foo({ dataType: 'a', day: 0 }); +>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 3)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 13, 15)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 13, 30)) + +[, b, ] = foo({ dataType: 'a', day: 0 }); +>b : Symbol(b, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 11)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 14, 15)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 14, 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, 17, 10)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 17, 26)) + +const [, , fails2] = foo(123); // Error: number not assignable to constraint +>fails2 : Symbol(fails2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 18, 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, 21, 5)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 21, 20)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 21, 35)) + +const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work +>explicit : Symbol(explicit, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 5)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 18)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 33)) +>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0)) +>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 66)) +>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 81)) + diff --git a/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types new file mode 100644 index 00000000000..2614d834b02 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.types @@ -0,0 +1,242 @@ +//// [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 +> : ^ + +declare function bar(template: T): [any, T, any]; +>bar : (template: T) => [any, T, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>dataType : "a" | "b" +> : ^^^^^^^^^ +>template : T +> : ^ + +// Test cases that should work (no excess property errors) +const [, works1] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +>works1 : 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 [, , works2] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>works2 : 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 [, , , works3] = foo({ dataType: 'a', day: 0 }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>works3 : 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 +> : ^ + +// Test with different function signatures +const [, , works4] = bar({ dataType: 'b', extra: 'value' }); +> : undefined +> : ^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>works4 : any +> : ^^^ +>bar({ dataType: 'b', extra: 'value' }) : [any, { dataType: "a" | "b"; }, any] +> : ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ +>bar : (template: T) => [any, T, any] +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ dataType: 'b', extra: 'value' } : { dataType: "b"; extra: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>dataType : "b" +> : ^^^ +>'b' : "b" +> : ^^^ +>extra : string +> : ^^^^^^ +>'value' : "value" +> : ^^^^^^^ + +// Test assignment destructuring +let a: any, b: any, c: any; +>a : any +> : ^^^ +>b : any +> : ^^^ +>c : any +> : ^^^ + +[, , a] = foo({ dataType: 'a', day: 0 }); +>[, , 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 +> : ^ + +[, b, ] = foo({ dataType: 'a', day: 0 }); +>[, b, ] = foo({ dataType: 'a', day: 0 }) : [{ dataType: "a"; day: number; }, any, any] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>[, b, ] : [undefined, any] +> : ^^^^^^^^^^^^^^^^ +> : undefined +> : ^^^^^^^^^ +>b : 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 +> : ^ + diff --git a/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts b/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts new file mode 100644 index 00000000000..6072b35918b --- /dev/null +++ b/tests/cases/compiler/excessPropertyCheckingInArrayDestructuring.ts @@ -0,0 +1,25 @@ +// @strict: true + +declare function foo(template: T): [T, any, any]; +declare function bar(template: T): [any, T, any]; + +// Test cases that should work (no excess property errors) +const [, works1] = foo({ dataType: 'a', day: 0 }); +const [, , works2] = foo({ dataType: 'a', day: 0 }); +const [, , , works3] = foo({ dataType: 'a', day: 0 }); + +// Test with different function signatures +const [, , works4] = bar({ dataType: 'b', extra: 'value' }); + +// Test assignment destructuring +let a: any, b: any, c: any; +[, , a] = foo({ dataType: 'a', day: 0 }); +[, b, ] = foo({ dataType: 'a', day: 0 }); + +// 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 \ No newline at end of file