Skip to content

Add implicit any errors for destructuring computed names which aren't late bound and have no corresponding index #23489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4227,19 +4227,42 @@ namespace ts {
const isLate = isLateBindableName(name);
const isWellKnown = isComputedPropertyName(name) && isWellKnownSymbolSyntactically(name.expression);
if (!isLate && !isWellKnown && isComputedNonLiteralName(name)) {
return anyType;
const exprType = checkExpression((name as ComputedPropertyName).expression);
if (isTypeAssignableToKind(exprType, TypeFlags.ESSymbolLike)) {
if (noImplicitAny) {
error(declaration, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(exprType), typeToString(parentType));
}
return anyType;
}
const indexerType = isTypeAssignableToKind(exprType, TypeFlags.NumberLike) && getIndexTypeOfType(parentType, IndexKind.Number) || getIndexTypeOfType(parentType, IndexKind.String);
if (!indexerType && noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
if (getIndexTypeOfType(parentType, IndexKind.Number)) {
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
}
else {
error(declaration, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(parentType));
}
}
return indexerType || anyType;
}

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

// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) {
parentType = getNonNullableType(parentType);
}
if (isLate && nameType && !getPropertyOfType(parentType, text) && isTypeAssignableToKind(nameType, TypeFlags.ESSymbolLike)) {
if (noImplicitAny) {
error(declaration, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(nameType), typeToString(parentType));
}
return anyType;
}
const declaredType = getConstraintForLocation(getTypeOfPropertyOfType(parentType, text), declaration.name);
type = declaredType && getFlowTypeOfReference(declaration, declaredType) ||
isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(2,15): error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(13,15): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(21,15): error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: number]: string; }'.
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(23,15): error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: string]: string; }'.
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(25,16): error TS2536: Type 'symbol' cannot be used to index type '{ [idx: number]: string; }'.
tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts(27,16): error TS2536: Type 'symbol' cannot be used to index type '{ [idx: string]: string; }'.


==== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts (6 errors) ====
let named = "foo";
let {[named]: prop} = {prop: "foo"};
~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{ prop: string; }' has no index signature.
void prop;

const numIndexed: {[idx: number]: string} = null as any;
const strIndexed: {[idx: string]: string} = null as any;

let numed = 6;

const symed = Symbol();
let symed2 = Symbol();

let {[named]: prop2} = numIndexed;
~~~~~
!!! error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
void prop2;
let {[numed]: prop3} = numIndexed;
void prop3;
let {[named]: prop4} = strIndexed;
void prop4;
let {[numed]: prop5} = strIndexed;
void prop5;
let {[symed]: prop6} = numIndexed;
~~~~~
!!! error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: number]: string; }'.
void prop6;
let {[symed]: prop7} = strIndexed;
~~~~~
!!! error TS2536: Type 'unique symbol' cannot be used to index type '{ [idx: string]: string; }'.
void prop7;
let {[symed2]: prop8} = numIndexed;
~~~~~
!!! error TS2536: Type 'symbol' cannot be used to index type '{ [idx: number]: string; }'.
void prop8;
let {[symed2]: prop9} = strIndexed;
~~~~~
!!! error TS2536: Type 'symbol' cannot be used to index type '{ [idx: string]: string; }'.
void prop9;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//// [lateBoundDestructuringImplicitAnyError.ts]
let named = "foo";
let {[named]: prop} = {prop: "foo"};
void prop;

const numIndexed: {[idx: number]: string} = null as any;
const strIndexed: {[idx: string]: string} = null as any;

let numed = 6;

const symed = Symbol();
let symed2 = Symbol();

let {[named]: prop2} = numIndexed;
void prop2;
let {[numed]: prop3} = numIndexed;
void prop3;
let {[named]: prop4} = strIndexed;
void prop4;
let {[numed]: prop5} = strIndexed;
void prop5;
let {[symed]: prop6} = numIndexed;
void prop6;
let {[symed]: prop7} = strIndexed;
void prop7;
let {[symed2]: prop8} = numIndexed;
void prop8;
let {[symed2]: prop9} = strIndexed;
void prop9;

//// [lateBoundDestructuringImplicitAnyError.js]
var named = "foo";
var _a = named, prop = { prop: "foo" }[_a];
void prop;
var numIndexed = null;
var strIndexed = null;
var numed = 6;
var symed = Symbol();
var symed2 = Symbol();
var _b = named, prop2 = numIndexed[_b];
void prop2;
var _c = numed, prop3 = numIndexed[_c];
void prop3;
var _d = named, prop4 = strIndexed[_d];
void prop4;
var _e = numed, prop5 = strIndexed[_e];
void prop5;
var _f = symed, prop6 = numIndexed[_f];
void prop6;
var _g = symed, prop7 = strIndexed[_g];
void prop7;
var _h = symed2, prop8 = numIndexed[_h];
void prop8;
var _j = symed2, prop9 = strIndexed[_j];
void prop9;
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
let named = "foo";
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))

let {[named]: prop} = {prop: "foo"};
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 23))

void prop;
>prop : Symbol(prop, Decl(lateBoundDestructuringImplicitAnyError.ts, 1, 5))

const numIndexed: {[idx: number]: string} = null as any;
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 20))

const strIndexed: {[idx: string]: string} = null as any;
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))
>idx : Symbol(idx, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 20))

let numed = 6;
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))

const symed = Symbol();
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --))

let symed2 = Symbol();
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --))

let {[named]: prop2} = numIndexed;
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 12, 5))
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))

void prop2;
>prop2 : Symbol(prop2, Decl(lateBoundDestructuringImplicitAnyError.ts, 12, 5))

let {[numed]: prop3} = numIndexed;
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 14, 5))
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))

void prop3;
>prop3 : Symbol(prop3, Decl(lateBoundDestructuringImplicitAnyError.ts, 14, 5))

let {[named]: prop4} = strIndexed;
>named : Symbol(named, Decl(lateBoundDestructuringImplicitAnyError.ts, 0, 3))
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 16, 5))
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))

void prop4;
>prop4 : Symbol(prop4, Decl(lateBoundDestructuringImplicitAnyError.ts, 16, 5))

let {[numed]: prop5} = strIndexed;
>numed : Symbol(numed, Decl(lateBoundDestructuringImplicitAnyError.ts, 7, 3))
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 18, 5))
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))

void prop5;
>prop5 : Symbol(prop5, Decl(lateBoundDestructuringImplicitAnyError.ts, 18, 5))

let {[symed]: prop6} = numIndexed;
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
>prop6 : Symbol(prop6, Decl(lateBoundDestructuringImplicitAnyError.ts, 20, 5))
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))

void prop6;
>prop6 : Symbol(prop6, Decl(lateBoundDestructuringImplicitAnyError.ts, 20, 5))

let {[symed]: prop7} = strIndexed;
>symed : Symbol(symed, Decl(lateBoundDestructuringImplicitAnyError.ts, 9, 5))
>prop7 : Symbol(prop7, Decl(lateBoundDestructuringImplicitAnyError.ts, 22, 5))
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))

void prop7;
>prop7 : Symbol(prop7, Decl(lateBoundDestructuringImplicitAnyError.ts, 22, 5))

let {[symed2]: prop8} = numIndexed;
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
>prop8 : Symbol(prop8, Decl(lateBoundDestructuringImplicitAnyError.ts, 24, 5))
>numIndexed : Symbol(numIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 4, 5))

void prop8;
>prop8 : Symbol(prop8, Decl(lateBoundDestructuringImplicitAnyError.ts, 24, 5))

let {[symed2]: prop9} = strIndexed;
>symed2 : Symbol(symed2, Decl(lateBoundDestructuringImplicitAnyError.ts, 10, 3))
>prop9 : Symbol(prop9, Decl(lateBoundDestructuringImplicitAnyError.ts, 26, 5))
>strIndexed : Symbol(strIndexed, Decl(lateBoundDestructuringImplicitAnyError.ts, 5, 5))

void prop9;
>prop9 : Symbol(prop9, Decl(lateBoundDestructuringImplicitAnyError.ts, 26, 5))

114 changes: 114 additions & 0 deletions tests/baselines/reference/lateBoundDestructuringImplicitAnyError.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
=== tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts ===
let named = "foo";
>named : string
>"foo" : "foo"

let {[named]: prop} = {prop: "foo"};
>named : string
>prop : any
>{prop: "foo"} : { prop: string; }
>prop : string
>"foo" : "foo"

void prop;
>void prop : undefined
>prop : any

const numIndexed: {[idx: number]: string} = null as any;
>numIndexed : { [idx: number]: string; }
>idx : number
>null as any : any
>null : null

const strIndexed: {[idx: string]: string} = null as any;
>strIndexed : { [idx: string]: string; }
>idx : string
>null as any : any
>null : null

let numed = 6;
>numed : number
>6 : 6

const symed = Symbol();
>symed : unique symbol
>Symbol() : unique symbol
>Symbol : SymbolConstructor

let symed2 = Symbol();
>symed2 : symbol
>Symbol() : symbol
>Symbol : SymbolConstructor

let {[named]: prop2} = numIndexed;
>named : string
>prop2 : any
>numIndexed : { [idx: number]: string; }

void prop2;
>void prop2 : undefined
>prop2 : any

let {[numed]: prop3} = numIndexed;
>numed : number
>prop3 : string
>numIndexed : { [idx: number]: string; }

void prop3;
>void prop3 : undefined
>prop3 : string

let {[named]: prop4} = strIndexed;
>named : string
>prop4 : string
>strIndexed : { [idx: string]: string; }

void prop4;
>void prop4 : undefined
>prop4 : string

let {[numed]: prop5} = strIndexed;
>numed : number
>prop5 : string
>strIndexed : { [idx: string]: string; }

void prop5;
>void prop5 : undefined
>prop5 : string

let {[symed]: prop6} = numIndexed;
>symed : unique symbol
>prop6 : any
>numIndexed : { [idx: number]: string; }

void prop6;
>void prop6 : undefined
>prop6 : any

let {[symed]: prop7} = strIndexed;
>symed : unique symbol
>prop7 : any
>strIndexed : { [idx: string]: string; }

void prop7;
>void prop7 : undefined
>prop7 : any

let {[symed2]: prop8} = numIndexed;
>symed2 : symbol
>prop8 : any
>numIndexed : { [idx: number]: string; }

void prop8;
>void prop8 : undefined
>prop8 : any

let {[symed2]: prop9} = strIndexed;
>symed2 : symbol
>prop9 : any
>strIndexed : { [idx: string]: string; }

void prop9;
>void prop9 : undefined
>prop9 : any

30 changes: 30 additions & 0 deletions tests/cases/compiler/lateBoundDestructuringImplicitAnyError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @lib: es6
// @noImplicitAny: true
let named = "foo";
let {[named]: prop} = {prop: "foo"};
void prop;

const numIndexed: {[idx: number]: string} = null as any;
const strIndexed: {[idx: string]: string} = null as any;

let numed = 6;

const symed = Symbol();
let symed2 = Symbol();

let {[named]: prop2} = numIndexed;
void prop2;
let {[numed]: prop3} = numIndexed;
void prop3;
let {[named]: prop4} = strIndexed;
void prop4;
let {[numed]: prop5} = strIndexed;
void prop5;
let {[symed]: prop6} = numIndexed;
void prop6;
let {[symed]: prop7} = strIndexed;
void prop7;
let {[symed2]: prop8} = numIndexed;
void prop8;
let {[symed2]: prop9} = strIndexed;
void prop9;