Skip to content

Commit 6d3b6e2

Browse files
authored
Add implicit any errors for destructuring computed names which aren't late bound and have no corresponding index (#23489)
* Add implicit any errors for destructuring computed names which arent late bound and have no corresponding index * Add string fallback for number * Expand test case, make functionality mimic other (buggy) areas * Add symbol name errors to bring in line with new indexing rules
1 parent 6c28da3 commit 6d3b6e2

6 files changed

+368
-2
lines changed

src/compiler/checker.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -4227,19 +4227,42 @@ namespace ts {
42274227
const isLate = isLateBindableName(name);
42284228
const isWellKnown = isComputedPropertyName(name) && isWellKnownSymbolSyntactically(name.expression);
42294229
if (!isLate && !isWellKnown && isComputedNonLiteralName(name)) {
4230-
return anyType;
4230+
const exprType = checkExpression((name as ComputedPropertyName).expression);
4231+
if (isTypeAssignableToKind(exprType, TypeFlags.ESSymbolLike)) {
4232+
if (noImplicitAny) {
4233+
error(declaration, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(exprType), typeToString(parentType));
4234+
}
4235+
return anyType;
4236+
}
4237+
const indexerType = isTypeAssignableToKind(exprType, TypeFlags.NumberLike) && getIndexTypeOfType(parentType, IndexKind.Number) || getIndexTypeOfType(parentType, IndexKind.String);
4238+
if (!indexerType && noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
4239+
if (getIndexTypeOfType(parentType, IndexKind.Number)) {
4240+
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
4241+
}
4242+
else {
4243+
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(parentType));
4244+
}
4245+
}
4246+
return indexerType || anyType;
42314247
}
42324248

42334249
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
42344250
// or otherwise the type of the string index signature.
4235-
const text = isLate ? getLateBoundNameFromType(checkComputedPropertyName(name as ComputedPropertyName) as LiteralType | UniqueESSymbolType) :
4251+
const nameType = isLate && checkComputedPropertyName(name as ComputedPropertyName) as LiteralType | UniqueESSymbolType;
4252+
const text = isLate ? getLateBoundNameFromType(nameType) :
42364253
isWellKnown ? getPropertyNameForKnownSymbolName(idText(((name as ComputedPropertyName).expression as PropertyAccessExpression).name)) :
42374254
getTextOfPropertyName(name);
42384255

42394256
// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
42404257
if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) {
42414258
parentType = getNonNullableType(parentType);
42424259
}
4260+
if (isLate && nameType && !getPropertyOfType(parentType, text) && isTypeAssignableToKind(nameType, TypeFlags.ESSymbolLike)) {
4261+
if (noImplicitAny) {
4262+
error(declaration, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(nameType), typeToString(parentType));
4263+
}
4264+
return anyType;
4265+
}
42434266
const declaredType = getConstraintForLocation(getTypeOfPropertyOfType(parentType, text), declaration.name);
42444267
type = declaredType && getFlowTypeOfReference(declaration, declaredType) ||
42454268
isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) ||
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(2,15): error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
2+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(13,15): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
3+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(21,15): error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: number]: string; }'.
4+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(23,15): error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: string]: string; }'.
5+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(25,16): error TS2536: Type 'symbol' cannot be used to index type '{ [idx: number]: string; }'.
6+
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(27,16): error TS2536: Type 'symbol' cannot be used to index type '{ [idx: string]: string; }'.
7+
8+
9+
==== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts (6 errors) ====
10+
let named = "foo";
11+
let {[named]: prop} = {prop: "foo"};
12+
~~~~
13+
!!! error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
14+
void prop;
15+
16+
const numIndexed: {[idx: number]: string} = null as any;
17+
const strIndexed: {[idx: string]: string} = null as any;
18+
19+
let numed = 6;
20+
21+
const symed = Symbol();
22+
let symed2 = Symbol();
23+
24+
let {[named]: prop2} = numIndexed;
25+
~~~~~
26+
!!! error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
27+
void prop2;
28+
let {[numed]: prop3} = numIndexed;
29+
void prop3;
30+
let {[named]: prop4} = strIndexed;
31+
void prop4;
32+
let {[numed]: prop5} = strIndexed;
33+
void prop5;
34+
let {[symed]: prop6} = numIndexed;
35+
~~~~~
36+
!!! error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: number]: string; }'.
37+
void prop6;
38+
let {[symed]: prop7} = strIndexed;
39+
~~~~~
40+
!!! error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: string]: string; }'.
41+
void prop7;
42+
let {[symed2]: prop8} = numIndexed;
43+
~~~~~
44+
!!! error TS2536: Type 'symbol' cannot be used to index type '{ [idx: number]: string; }'.
45+
void prop8;
46+
let {[symed2]: prop9} = strIndexed;
47+
~~~~~
48+
!!! error TS2536: Type 'symbol' cannot be used to index type '{ [idx: string]: string; }'.
49+
void prop9;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//// [lateBoundDestructuringImplicitAnyError.ts]
2+
let named = "foo";
3+
let {[named]: prop} = {prop: "foo"};
4+
void prop;
5+
6+
const numIndexed: {[idx: number]: string} = null as any;
7+
const strIndexed: {[idx: string]: string} = null as any;
8+
9+
let numed = 6;
10+
11+
const symed = Symbol();
12+
let symed2 = Symbol();
13+
14+
let {[named]: prop2} = numIndexed;
15+
void prop2;
16+
let {[numed]: prop3} = numIndexed;
17+
void prop3;
18+
let {[named]: prop4} = strIndexed;
19+
void prop4;
20+
let {[numed]: prop5} = strIndexed;
21+
void prop5;
22+
let {[symed]: prop6} = numIndexed;
23+
void prop6;
24+
let {[symed]: prop7} = strIndexed;
25+
void prop7;
26+
let {[symed2]: prop8} = numIndexed;
27+
void prop8;
28+
let {[symed2]: prop9} = strIndexed;
29+
void prop9;
30+
31+
//// [lateBoundDestructuringImplicitAnyError.js]
32+
var named = "foo";
33+
var _a = named, prop = { prop: "foo" }[_a];
34+
void prop;
35+
var numIndexed = null;
36+
var strIndexed = null;
37+
var numed = 6;
38+
var symed = Symbol();
39+
var symed2 = Symbol();
40+
var _b = named, prop2 = numIndexed[_b];
41+
void prop2;
42+
var _c = numed, prop3 = numIndexed[_c];
43+
void prop3;
44+
var _d = named, prop4 = strIndexed[_d];
45+
void prop4;
46+
var _e = numed, prop5 = strIndexed[_e];
47+
void prop5;
48+
var _f = symed, prop6 = numIndexed[_f];
49+
void prop6;
50+
var _g = symed, prop7 = strIndexed[_g];
51+
void prop7;
52+
var _h = symed2, prop8 = numIndexed[_h];
53+
void prop8;
54+
var _j = symed2, prop9 = strIndexed[_j];
55+
void prop9;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
2+
let named = "foo";
3+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
4+
5+
let {[named]: prop} = {prop: "foo"};
6+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
7+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))
8+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 23))
9+
10+
void prop;
11+
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))
12+
13+
const numIndexed: {[idx: number]: string} = null as any;
14+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
15+
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 20))
16+
17+
const strIndexed: {[idx: string]: string} = null as any;
18+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
19+
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 20))
20+
21+
let numed = 6;
22+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
23+
24+
const symed = Symbol();
25+
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
26+
>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --))
27+
28+
let symed2 = Symbol();
29+
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
30+
>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --))
31+
32+
let {[named]: prop2} = numIndexed;
33+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
34+
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 12, 5))
35+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
36+
37+
void prop2;
38+
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 12, 5))
39+
40+
let {[numed]: prop3} = numIndexed;
41+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
42+
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 14, 5))
43+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
44+
45+
void prop3;
46+
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 14, 5))
47+
48+
let {[named]: prop4} = strIndexed;
49+
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
50+
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 16, 5))
51+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
52+
53+
void prop4;
54+
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 16, 5))
55+
56+
let {[numed]: prop5} = strIndexed;
57+
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
58+
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 18, 5))
59+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
60+
61+
void prop5;
62+
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 18, 5))
63+
64+
let {[symed]: prop6} = numIndexed;
65+
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
66+
>prop6 : Symbol(prop6, Decl(lateBoundDestructuringImplicitAnyError.ts, 20, 5))
67+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
68+
69+
void prop6;
70+
>prop6 : Symbol(prop6, Decl(lateBoundDestructuringImplicitAnyError.ts, 20, 5))
71+
72+
let {[symed]: prop7} = strIndexed;
73+
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
74+
>prop7 : Symbol(prop7, Decl(lateBoundDestructuringImplicitAnyError.ts, 22, 5))
75+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
76+
77+
void prop7;
78+
>prop7 : Symbol(prop7, Decl(lateBoundDestructuringImplicitAnyError.ts, 22, 5))
79+
80+
let {[symed2]: prop8} = numIndexed;
81+
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
82+
>prop8 : Symbol(prop8, Decl(lateBoundDestructuringImplicitAnyError.ts, 24, 5))
83+
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
84+
85+
void prop8;
86+
>prop8 : Symbol(prop8, Decl(lateBoundDestructuringImplicitAnyError.ts, 24, 5))
87+
88+
let {[symed2]: prop9} = strIndexed;
89+
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
90+
>prop9 : Symbol(prop9, Decl(lateBoundDestructuringImplicitAnyError.ts, 26, 5))
91+
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
92+
93+
void prop9;
94+
>prop9 : Symbol(prop9, Decl(lateBoundDestructuringImplicitAnyError.ts, 26, 5))
95+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
2+
let named = "foo";
3+
>named : string
4+
>"foo" : "foo"
5+
6+
let {[named]: prop} = {prop: "foo"};
7+
>named : string
8+
>prop : any
9+
>{prop: "foo"} : { prop: string; }
10+
>prop : string
11+
>"foo" : "foo"
12+
13+
void prop;
14+
>void prop : undefined
15+
>prop : any
16+
17+
const numIndexed: {[idx: number]: string} = null as any;
18+
>numIndexed : { [idx: number]: string; }
19+
>idx : number
20+
>null as any : any
21+
>null : null
22+
23+
const strIndexed: {[idx: string]: string} = null as any;
24+
>strIndexed : { [idx: string]: string; }
25+
>idx : string
26+
>null as any : any
27+
>null : null
28+
29+
let numed = 6;
30+
>numed : number
31+
>6 : 6
32+
33+
const symed = Symbol();
34+
>symed : unique symbol
35+
>Symbol() : unique symbol
36+
>Symbol : SymbolConstructor
37+
38+
let symed2 = Symbol();
39+
>symed2 : symbol
40+
>Symbol() : symbol
41+
>Symbol : SymbolConstructor
42+
43+
let {[named]: prop2} = numIndexed;
44+
>named : string
45+
>prop2 : any
46+
>numIndexed : { [idx: number]: string; }
47+
48+
void prop2;
49+
>void prop2 : undefined
50+
>prop2 : any
51+
52+
let {[numed]: prop3} = numIndexed;
53+
>numed : number
54+
>prop3 : string
55+
>numIndexed : { [idx: number]: string; }
56+
57+
void prop3;
58+
>void prop3 : undefined
59+
>prop3 : string
60+
61+
let {[named]: prop4} = strIndexed;
62+
>named : string
63+
>prop4 : string
64+
>strIndexed : { [idx: string]: string; }
65+
66+
void prop4;
67+
>void prop4 : undefined
68+
>prop4 : string
69+
70+
let {[numed]: prop5} = strIndexed;
71+
>numed : number
72+
>prop5 : string
73+
>strIndexed : { [idx: string]: string; }
74+
75+
void prop5;
76+
>void prop5 : undefined
77+
>prop5 : string
78+
79+
let {[symed]: prop6} = numIndexed;
80+
>symed : unique symbol
81+
>prop6 : any
82+
>numIndexed : { [idx: number]: string; }
83+
84+
void prop6;
85+
>void prop6 : undefined
86+
>prop6 : any
87+
88+
let {[symed]: prop7} = strIndexed;
89+
>symed : unique symbol
90+
>prop7 : any
91+
>strIndexed : { [idx: string]: string; }
92+
93+
void prop7;
94+
>void prop7 : undefined
95+
>prop7 : any
96+
97+
let {[symed2]: prop8} = numIndexed;
98+
>symed2 : symbol
99+
>prop8 : any
100+
>numIndexed : { [idx: number]: string; }
101+
102+
void prop8;
103+
>void prop8 : undefined
104+
>prop8 : any
105+
106+
let {[symed2]: prop9} = strIndexed;
107+
>symed2 : symbol
108+
>prop9 : any
109+
>strIndexed : { [idx: string]: string; }
110+
111+
void prop9;
112+
>void prop9 : undefined
113+
>prop9 : any
114+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @lib: es6
2+
// @noImplicitAny: true
3+
let named = "foo";
4+
let {[named]: prop} = {prop: "foo"};
5+
void prop;
6+
7+
const numIndexed: {[idx: number]: string} = null as any;
8+
const strIndexed: {[idx: string]: string} = null as any;
9+
10+
let numed = 6;
11+
12+
const symed = Symbol();
13+
let symed2 = Symbol();
14+
15+
let {[named]: prop2} = numIndexed;
16+
void prop2;
17+
let {[numed]: prop3} = numIndexed;
18+
void prop3;
19+
let {[named]: prop4} = strIndexed;
20+
void prop4;
21+
let {[numed]: prop5} = strIndexed;
22+
void prop5;
23+
let {[symed]: prop6} = numIndexed;
24+
void prop6;
25+
let {[symed]: prop7} = strIndexed;
26+
void prop7;
27+
let {[symed2]: prop8} = numIndexed;
28+
void prop8;
29+
let {[symed2]: prop9} = strIndexed;
30+
void prop9;

0 commit comments

Comments
 (0)