Skip to content

Commit 2d1cf66

Browse files
Make conservative fix for specific array destructuring position
After differential debugging, narrowed the fix to only handle the specific problematic case: destructuring where exactly the third element (index 2) is accessed with the pattern `[, , element]`. This prevents performance issues while still fixing the core excess property checking bug identified in #41548. Co-authored-by: RyanCavanaugh <[email protected]>
1 parent c2434b6 commit 2d1cf66

6 files changed

Lines changed: 154 additions & 239 deletions

src/compiler/checker.ts

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -36421,39 +36421,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3642136421
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
3642236422
}
3642336423

36424-
function isCallInProblematicDestructuringContext(node: CallLikeExpression): boolean {
36425-
// Check if this call expression is used as the initializer in a variable declaration with a destructuring pattern
36426-
const parent = node.parent;
36427-
if (parent && isVariableDeclaration(parent) && parent.initializer === node) {
36428-
if (isArrayBindingPattern(parent.name)) {
36429-
// Check if we're destructuring at a position that causes inference issues
36430-
// Based on investigation, positions like 2, 4, 7, etc. can cause problems
36431-
const elements = parent.name.elements;
36432-
for (let i = 0; i < elements.length; i++) {
36433-
const element = elements[i];
36434-
if (!isOmittedExpression(element) && i >= 2) {
36435-
// Position 2 and higher can trigger the issue
36436-
return true;
36437-
}
36438-
}
36439-
}
36440-
}
36441-
36442-
// Check for assignment expressions: [a, b, c] = foo()
36443-
if (parent && isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken && parent.right === node) {
36444-
if (isArrayLiteralExpression(parent.left)) {
36445-
// Similar check for assignment destructuring
36446-
const elements = parent.left.elements;
36447-
for (let i = 0; i < elements.length; i++) {
36448-
const element = elements[i];
36449-
if (!isOmittedExpression(element) && i >= 2) {
36450-
return true;
36451-
}
36452-
}
36453-
}
36454-
}
36455-
36456-
return false;
36424+
function isCallInProblematicDestructuringContext(node: CallLikeExpression): boolean {
36425+
// Check if this call expression is used as the initializer in a variable declaration with a destructuring pattern
36426+
const parent = node.parent;
36427+
if (parent && isVariableDeclaration(parent) && parent.initializer === node) {
36428+
if (isArrayBindingPattern(parent.name)) {
36429+
// Only apply this fix for the specific known problematic case:
36430+
// destructuring where the third position (index 2) is accessed
36431+
const elements = parent.name.elements;
36432+
return elements.length === 3 &&
36433+
isOmittedExpression(elements[0]) &&
36434+
isOmittedExpression(elements[1]) &&
36435+
!isOmittedExpression(elements[2]);
36436+
}
36437+
}
36438+
36439+
return false;
3645736440
}
3645836441

3645936442
function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, callChainFlags: SignatureFlags, headMessage?: DiagnosticMessage): Signature {
Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
1-
excessPropertyCheckingInArrayDestructuring.ts(7,14): error TS2493: Tuple type '[{ dataType: "a"; day: number; }, any, any]' of length '3' has no element at index '3'.
2-
excessPropertyCheckingInArrayDestructuring.ts(18,28): error TS2322: Type '"c"' is not assignable to type '"a" | "b"'.
3-
excessPropertyCheckingInArrayDestructuring.ts(19,26): error TS2345: Argument of type 'number' is not assignable to parameter of type '{ dataType: "a" | "b"; }'.
1+
excessPropertyCheckingInArrayDestructuring.ts(12,28): error TS2322: Type '"c"' is not assignable to type '"a" | "b"'.
2+
excessPropertyCheckingInArrayDestructuring.ts(13,26): error TS2345: Argument of type 'number' is not assignable to parameter of type '{ dataType: "a" | "b"; }'.
43

54

6-
==== excessPropertyCheckingInArrayDestructuring.ts (3 errors) ====
5+
==== excessPropertyCheckingInArrayDestructuring.ts (2 errors) ====
76
declare function foo<T extends { dataType: 'a' | 'b' }>(template: T): [T, any, any];
8-
declare function bar<T extends { dataType: 'a' | 'b' }>(template: T): [any, T, any];
97

10-
// Test cases that should work (no excess property errors)
11-
const [, works1] = foo({ dataType: 'a', day: 0 });
12-
const [, , works2] = foo({ dataType: 'a', day: 0 });
13-
const [, , , works3] = foo({ dataType: 'a', day: 0 });
14-
~~~~~~
15-
!!! error TS2493: Tuple type '[{ dataType: "a"; day: number; }, any, any]' of length '3' has no element at index '3'.
8+
// Test the specific problematic case that should now work
9+
const [, , works1] = foo({ dataType: 'a', day: 0 });
10+
const [, , works2] = foo({ dataType: 'b', extra: 'value' });
1611

17-
// Test with different function signatures
18-
const [, , works4] = bar({ dataType: 'b', extra: 'value' });
19-
20-
// Test assignment destructuring
21-
let a: any, b: any, c: any;
22-
[, , a] = foo({ dataType: 'a', day: 0 });
23-
[, b, ] = foo({ dataType: 'a', day: 0 });
12+
// Test assignment destructuring (not currently fixed)
13+
let a: any;
14+
[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error
2415

2516
// Test that legitimate errors are still caught
2617
const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b'
@@ -33,4 +24,8 @@ excessPropertyCheckingInArrayDestructuring.ts(19,26): error TS2345: Argument of
3324

3425
// Test that non-destructuring cases work as before
3526
const result = foo({ dataType: 'a', day: 0 }); // Should work
36-
const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work
27+
const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work
28+
29+
// Test that other destructuring patterns work correctly
30+
const [first] = foo({ dataType: 'a', day: 0 }); // Should work
31+
const [, second] = foo({ dataType: 'a', day: 0 }); // Should work

tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.js

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,42 @@
22

33
//// [excessPropertyCheckingInArrayDestructuring.ts]
44
declare function foo<T extends { dataType: 'a' | 'b' }>(template: T): [T, any, any];
5-
declare function bar<T extends { dataType: 'a' | 'b' }>(template: T): [any, T, any];
65

7-
// Test cases that should work (no excess property errors)
8-
const [, works1] = foo({ dataType: 'a', day: 0 });
9-
const [, , works2] = foo({ dataType: 'a', day: 0 });
10-
const [, , , works3] = foo({ dataType: 'a', day: 0 });
6+
// Test the specific problematic case that should now work
7+
const [, , works1] = foo({ dataType: 'a', day: 0 });
8+
const [, , works2] = foo({ dataType: 'b', extra: 'value' });
119

12-
// Test with different function signatures
13-
const [, , works4] = bar({ dataType: 'b', extra: 'value' });
14-
15-
// Test assignment destructuring
16-
let a: any, b: any, c: any;
17-
[, , a] = foo({ dataType: 'a', day: 0 });
18-
[, b, ] = foo({ dataType: 'a', day: 0 });
10+
// Test assignment destructuring (not currently fixed)
11+
let a: any;
12+
[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error
1913

2014
// Test that legitimate errors are still caught
2115
const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b'
2216
const [, , fails2] = foo(123); // Error: number not assignable to constraint
2317

2418
// Test that non-destructuring cases work as before
2519
const result = foo({ dataType: 'a', day: 0 }); // Should work
26-
const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work
20+
const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work
21+
22+
// Test that other destructuring patterns work correctly
23+
const [first] = foo({ dataType: 'a', day: 0 }); // Should work
24+
const [, second] = foo({ dataType: 'a', day: 0 }); // Should work
2725

2826
//// [excessPropertyCheckingInArrayDestructuring.js]
2927
"use strict";
30-
var _a, _b;
31-
// Test cases that should work (no excess property errors)
32-
var _c = foo({ dataType: 'a', day: 0 }), works1 = _c[1];
33-
var _d = foo({ dataType: 'a', day: 0 }), works2 = _d[2];
34-
var _e = foo({ dataType: 'a', day: 0 }), works3 = _e[3];
35-
// Test with different function signatures
36-
var _f = bar({ dataType: 'b', extra: 'value' }), works4 = _f[2];
37-
// Test assignment destructuring
38-
var a, b, c;
39-
_a = foo({ dataType: 'a', day: 0 }), a = _a[2];
40-
_b = foo({ dataType: 'a', day: 0 }), b = _b[1];
28+
var _a;
29+
// Test the specific problematic case that should now work
30+
var _b = foo({ dataType: 'a', day: 0 }), works1 = _b[2];
31+
var _c = foo({ dataType: 'b', extra: 'value' }), works2 = _c[2];
32+
// Test assignment destructuring (not currently fixed)
33+
var a;
34+
_a = foo({ dataType: 'a', day: 0 }), a = _a[2]; // This might still error
4135
// Test that legitimate errors are still caught
42-
var _g = foo({ dataType: 'c' }), fails1 = _g[2]; // Error: 'c' not assignable to 'a' | 'b'
43-
var _h = foo(123), fails2 = _h[2]; // Error: number not assignable to constraint
36+
var _d = foo({ dataType: 'c' }), fails1 = _d[2]; // Error: 'c' not assignable to 'a' | 'b'
37+
var _e = foo(123), fails2 = _e[2]; // Error: number not assignable to constraint
4438
// Test that non-destructuring cases work as before
4539
var result = foo({ dataType: 'a', day: 0 }); // Should work
4640
var explicit = foo({ dataType: 'a', day: 0 }); // Should work
41+
// Test that other destructuring patterns work correctly
42+
var first = foo({ dataType: 'a', day: 0 })[0]; // Should work
43+
var _f = foo({ dataType: 'a', day: 0 }), second = _f[1]; // Should work

tests/baselines/reference/excessPropertyCheckingInArrayDestructuring.symbols

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,80 +9,64 @@ declare function foo<T extends { dataType: 'a' | 'b' }>(template: T): [T, any, a
99
>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 21))
1010
>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 21))
1111

12-
declare function bar<T extends { dataType: 'a' | 'b' }>(template: T): [any, T, any];
13-
>bar : Symbol(bar, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 84))
14-
>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21))
15-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 32))
16-
>template : Symbol(template, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 56))
17-
>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21))
18-
>T : Symbol(T, Decl(excessPropertyCheckingInArrayDestructuring.ts, 1, 21))
19-
20-
// Test cases that should work (no excess property errors)
21-
const [, works1] = foo({ dataType: 'a', day: 0 });
22-
>works1 : Symbol(works1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 8))
23-
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
24-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 24))
25-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 39))
26-
27-
const [, , works2] = foo({ dataType: 'a', day: 0 });
28-
>works2 : Symbol(works2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 10))
12+
// Test the specific problematic case that should now work
13+
const [, , works1] = foo({ dataType: 'a', day: 0 });
14+
>works1 : Symbol(works1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 10))
2915
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
30-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 26))
31-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 5, 41))
16+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 26))
17+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 3, 41))
3218

33-
const [, , , works3] = foo({ dataType: 'a', day: 0 });
34-
>works3 : Symbol(works3, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 12))
19+
const [, , works2] = foo({ dataType: 'b', extra: 'value' });
20+
>works2 : Symbol(works2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 10))
3521
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
36-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 28))
37-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 6, 43))
38-
39-
// Test with different function signatures
40-
const [, , works4] = bar({ dataType: 'b', extra: 'value' });
41-
>works4 : Symbol(works4, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 10))
42-
>bar : Symbol(bar, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 84))
43-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 26))
44-
>extra : Symbol(extra, Decl(excessPropertyCheckingInArrayDestructuring.ts, 9, 41))
45-
46-
// Test assignment destructuring
47-
let a: any, b: any, c: any;
48-
>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 3))
49-
>b : Symbol(b, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 11))
50-
>c : Symbol(c, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 19))
22+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 26))
23+
>extra : Symbol(extra, Decl(excessPropertyCheckingInArrayDestructuring.ts, 4, 41))
5124

52-
[, , a] = foo({ dataType: 'a', day: 0 });
53-
>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 3))
54-
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
55-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 13, 15))
56-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 13, 30))
25+
// Test assignment destructuring (not currently fixed)
26+
let a: any;
27+
>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 7, 3))
5728

58-
[, b, ] = foo({ dataType: 'a', day: 0 });
59-
>b : Symbol(b, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 11))
29+
[, , a] = foo({ dataType: 'a', day: 0 }); // This might still error
30+
>a : Symbol(a, Decl(excessPropertyCheckingInArrayDestructuring.ts, 7, 3))
6031
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
61-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 14, 15))
62-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 14, 30))
32+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 8, 15))
33+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 8, 30))
6334

6435
// Test that legitimate errors are still caught
6536
const [, , fails1] = foo({ dataType: 'c' }); // Error: 'c' not assignable to 'a' | 'b'
66-
>fails1 : Symbol(fails1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 17, 10))
37+
>fails1 : Symbol(fails1, Decl(excessPropertyCheckingInArrayDestructuring.ts, 11, 10))
6738
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
68-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 17, 26))
39+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 11, 26))
6940

7041
const [, , fails2] = foo(123); // Error: number not assignable to constraint
71-
>fails2 : Symbol(fails2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 18, 10))
42+
>fails2 : Symbol(fails2, Decl(excessPropertyCheckingInArrayDestructuring.ts, 12, 10))
7243
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
7344

7445
// Test that non-destructuring cases work as before
7546
const result = foo({ dataType: 'a', day: 0 }); // Should work
76-
>result : Symbol(result, Decl(excessPropertyCheckingInArrayDestructuring.ts, 21, 5))
47+
>result : Symbol(result, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 5))
7748
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
78-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 21, 20))
79-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 21, 35))
49+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 20))
50+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 15, 35))
8051

8152
const explicit: [{ dataType: 'a', day: number }, any, any] = foo({ dataType: 'a', day: 0 }); // Should work
82-
>explicit : Symbol(explicit, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 5))
83-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 18))
84-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 33))
53+
>explicit : Symbol(explicit, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 5))
54+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 18))
55+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 33))
56+
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
57+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 66))
58+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 16, 81))
59+
60+
// Test that other destructuring patterns work correctly
61+
const [first] = foo({ dataType: 'a', day: 0 }); // Should work
62+
>first : Symbol(first, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 7))
63+
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
64+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 21))
65+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 19, 36))
66+
67+
const [, second] = foo({ dataType: 'a', day: 0 }); // Should work
68+
>second : Symbol(second, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 8))
8569
>foo : Symbol(foo, Decl(excessPropertyCheckingInArrayDestructuring.ts, 0, 0))
86-
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 66))
87-
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 22, 81))
70+
>dataType : Symbol(dataType, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 24))
71+
>day : Symbol(day, Decl(excessPropertyCheckingInArrayDestructuring.ts, 20, 39))
8872

0 commit comments

Comments
 (0)