Add index signature for anonymous object literal type (#37903)

* Use ts.map for stylistic consistency

* Show error only if noImplicitAny is set

* Accept baseline for noImplicitAnyIndexing

* Fix lint error

* Add test cases for indexedAccessWithFreshObjectLiteral
This commit is contained in:
okmttdhr 2020-11-03 07:35:56 +09:00 committed by GitHub
parent f646ec87fc
commit 7db5f68144
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 608 additions and 32 deletions

View file

@ -13878,6 +13878,19 @@ namespace ts {
return anyType;
}
if (accessExpression && !isConstEnumObjectType(objectType)) {
if (isObjectLiteralType(objectType)) {
if (noImplicitAny && indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
diagnostics.add(createDiagnosticForNode(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType)));
return undefinedType;
}
else if (indexType.flags & (TypeFlags.Number | TypeFlags.String)) {
const types = map((<ResolvedType>objectType).properties, property => {
return getTypeOfSymbol(property);
});
return getUnionType(append(types, undefinedType));
}
}
if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) {
error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType));
}

View file

@ -0,0 +1,64 @@
tests/cases/compiler/indexedAccessWithFreshObjectLiteral.ts(34,10): error TS2339: Property 'z' does not exist on type '{ a: number; b: string; c: boolean; }'.
==== tests/cases/compiler/indexedAccessWithFreshObjectLiteral.ts (1 errors) ====
function foo (id: string) {
return {
a: 1,
b: "",
c: true
}[id]
}
function bar (id: 'a' | 'b') {
return {
a: 1,
b: "",
c: false
}[id]
}
function baz (id: '1' | '2') {
return {
1: 1,
2: "",
3: false
}[id]
}
function qux (id: 1 | 2) {
return {
1: 1,
2: "",
3: false
}[id]
}
function quux (id: 'a' | 'b' | 'z') {
return {
~
a: 1,
~~~~~~~~~~~
b: "",
~~~~~~~~~~~~
c: false
~~~~~~~~~~~~~~
}[id]
~~~~~~~
!!! error TS2339: Property 'z' does not exist on type '{ a: number; b: string; c: boolean; }'.
}
function corge(id: string) {
return ({
a: 123,
b: ""
} as Record<string, number | string>)[id]
}
function grault(id: string) {
return ({
a: 123,
b: ""
} as { [k: string]: string | number})[id]
}

View file

@ -0,0 +1,105 @@
//// [indexedAccessWithFreshObjectLiteral.ts]
function foo (id: string) {
return {
a: 1,
b: "",
c: true
}[id]
}
function bar (id: 'a' | 'b') {
return {
a: 1,
b: "",
c: false
}[id]
}
function baz (id: '1' | '2') {
return {
1: 1,
2: "",
3: false
}[id]
}
function qux (id: 1 | 2) {
return {
1: 1,
2: "",
3: false
}[id]
}
function quux (id: 'a' | 'b' | 'z') {
return {
a: 1,
b: "",
c: false
}[id]
}
function corge(id: string) {
return ({
a: 123,
b: ""
} as Record<string, number | string>)[id]
}
function grault(id: string) {
return ({
a: 123,
b: ""
} as { [k: string]: string | number})[id]
}
//// [indexedAccessWithFreshObjectLiteral.js]
"use strict";
function foo(id) {
return {
a: 1,
b: "",
c: true
}[id];
}
function bar(id) {
return {
a: 1,
b: "",
c: false
}[id];
}
function baz(id) {
return {
1: 1,
2: "",
3: false
}[id];
}
function qux(id) {
return {
1: 1,
2: "",
3: false
}[id];
}
function quux(id) {
return {
a: 1,
b: "",
c: false
}[id];
}
function corge(id) {
return {
a: 123,
b: ""
}[id];
}
function grault(id) {
return {
a: 123,
b: ""
}[id];
}

View file

@ -0,0 +1,123 @@
=== tests/cases/compiler/indexedAccessWithFreshObjectLiteral.ts ===
function foo (id: string) {
>foo : Symbol(foo, Decl(indexedAccessWithFreshObjectLiteral.ts, 0, 0))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 0, 14))
return {
a: 1,
>a : Symbol(a, Decl(indexedAccessWithFreshObjectLiteral.ts, 1, 10))
b: "",
>b : Symbol(b, Decl(indexedAccessWithFreshObjectLiteral.ts, 2, 11))
c: true
>c : Symbol(c, Decl(indexedAccessWithFreshObjectLiteral.ts, 3, 12))
}[id]
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 0, 14))
}
function bar (id: 'a' | 'b') {
>bar : Symbol(bar, Decl(indexedAccessWithFreshObjectLiteral.ts, 6, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 8, 14))
return {
a: 1,
>a : Symbol(a, Decl(indexedAccessWithFreshObjectLiteral.ts, 9, 10))
b: "",
>b : Symbol(b, Decl(indexedAccessWithFreshObjectLiteral.ts, 10, 11))
c: false
>c : Symbol(c, Decl(indexedAccessWithFreshObjectLiteral.ts, 11, 12))
}[id]
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 8, 14))
}
function baz (id: '1' | '2') {
>baz : Symbol(baz, Decl(indexedAccessWithFreshObjectLiteral.ts, 14, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 16, 14))
return {
1: 1,
>1 : Symbol(1, Decl(indexedAccessWithFreshObjectLiteral.ts, 17, 10))
2: "",
>2 : Symbol(2, Decl(indexedAccessWithFreshObjectLiteral.ts, 18, 11))
3: false
>3 : Symbol(3, Decl(indexedAccessWithFreshObjectLiteral.ts, 19, 12))
}[id]
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 16, 14))
}
function qux (id: 1 | 2) {
>qux : Symbol(qux, Decl(indexedAccessWithFreshObjectLiteral.ts, 22, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 24, 14))
return {
1: 1,
>1 : Symbol(1, Decl(indexedAccessWithFreshObjectLiteral.ts, 25, 10))
2: "",
>2 : Symbol(2, Decl(indexedAccessWithFreshObjectLiteral.ts, 26, 11))
3: false
>3 : Symbol(3, Decl(indexedAccessWithFreshObjectLiteral.ts, 27, 12))
}[id]
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 24, 14))
}
function quux (id: 'a' | 'b' | 'z') {
>quux : Symbol(quux, Decl(indexedAccessWithFreshObjectLiteral.ts, 30, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 32, 15))
return {
a: 1,
>a : Symbol(a, Decl(indexedAccessWithFreshObjectLiteral.ts, 33, 10))
b: "",
>b : Symbol(b, Decl(indexedAccessWithFreshObjectLiteral.ts, 34, 11))
c: false
>c : Symbol(c, Decl(indexedAccessWithFreshObjectLiteral.ts, 35, 12))
}[id]
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 32, 15))
}
function corge(id: string) {
>corge : Symbol(corge, Decl(indexedAccessWithFreshObjectLiteral.ts, 38, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 40, 15))
return ({
a: 123,
>a : Symbol(a, Decl(indexedAccessWithFreshObjectLiteral.ts, 41, 11))
b: ""
>b : Symbol(b, Decl(indexedAccessWithFreshObjectLiteral.ts, 42, 13))
} as Record<string, number | string>)[id]
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 40, 15))
}
function grault(id: string) {
>grault : Symbol(grault, Decl(indexedAccessWithFreshObjectLiteral.ts, 45, 1))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 47, 16))
return ({
a: 123,
>a : Symbol(a, Decl(indexedAccessWithFreshObjectLiteral.ts, 48, 11))
b: ""
>b : Symbol(b, Decl(indexedAccessWithFreshObjectLiteral.ts, 49, 13))
} as { [k: string]: string | number})[id]
>k : Symbol(k, Decl(indexedAccessWithFreshObjectLiteral.ts, 51, 10))
>id : Symbol(id, Decl(indexedAccessWithFreshObjectLiteral.ts, 47, 16))
}

View file

@ -0,0 +1,166 @@
=== tests/cases/compiler/indexedAccessWithFreshObjectLiteral.ts ===
function foo (id: string) {
>foo : (id: string) => string | number | boolean | undefined
>id : string
return {
>{ a: 1, b: "", c: true }[id] : string | number | boolean | undefined
>{ a: 1, b: "", c: true } : { a: number; b: string; c: boolean; }
a: 1,
>a : number
>1 : 1
b: "",
>b : string
>"" : ""
c: true
>c : boolean
>true : true
}[id]
>id : string
}
function bar (id: 'a' | 'b') {
>bar : (id: 'a' | 'b') => string | number
>id : "a" | "b"
return {
>{ a: 1, b: "", c: false }[id] : string | number
>{ a: 1, b: "", c: false } : { a: number; b: string; c: boolean; }
a: 1,
>a : number
>1 : 1
b: "",
>b : string
>"" : ""
c: false
>c : boolean
>false : false
}[id]
>id : "a" | "b"
}
function baz (id: '1' | '2') {
>baz : (id: '1' | '2') => string | number
>id : "1" | "2"
return {
>{ 1: 1, 2: "", 3: false }[id] : string | number
>{ 1: 1, 2: "", 3: false } : { 1: number; 2: string; 3: boolean; }
1: 1,
>1 : number
>1 : 1
2: "",
>2 : string
>"" : ""
3: false
>3 : boolean
>false : false
}[id]
>id : "1" | "2"
}
function qux (id: 1 | 2) {
>qux : (id: 1 | 2) => string | number
>id : 1 | 2
return {
>{ 1: 1, 2: "", 3: false }[id] : string | number
>{ 1: 1, 2: "", 3: false } : { 1: number; 2: string; 3: boolean; }
1: 1,
>1 : number
>1 : 1
2: "",
>2 : string
>"" : ""
3: false
>3 : boolean
>false : false
}[id]
>id : 1 | 2
}
function quux (id: 'a' | 'b' | 'z') {
>quux : (id: 'a' | 'b' | 'z') => string | number | undefined
>id : "a" | "b" | "z"
return {
>{ a: 1, b: "", c: false }[id] : string | number | undefined
>{ a: 1, b: "", c: false } : { a: number; b: string; c: boolean; }
a: 1,
>a : number
>1 : 1
b: "",
>b : string
>"" : ""
c: false
>c : boolean
>false : false
}[id]
>id : "a" | "b" | "z"
}
function corge(id: string) {
>corge : (id: string) => string | number
>id : string
return ({
>({ a: 123, b: "" } as Record<string, number | string>)[id] : string | number
>({ a: 123, b: "" } as Record<string, number | string>) : Record<string, string | number>
>{ a: 123, b: "" } as Record<string, number | string> : Record<string, string | number>
>{ a: 123, b: "" } : { a: number; b: string; }
a: 123,
>a : number
>123 : 123
b: ""
>b : string
>"" : ""
} as Record<string, number | string>)[id]
>id : string
}
function grault(id: string) {
>grault : (id: string) => string | number
>id : string
return ({
>({ a: 123, b: "" } as { [k: string]: string | number})[id] : string | number
>({ a: 123, b: "" } as { [k: string]: string | number}) : { [k: string]: string | number; }
>{ a: 123, b: "" } as { [k: string]: string | number} : { [k: string]: string | number; }
>{ a: 123, b: "" } : { a: number; b: string; }
a: 123,
>a : number
>123 : 123
b: ""
>b : string
>"" : ""
} as { [k: string]: string | number})[id]
>k : string
>id : string
}

View file

@ -1,8 +1,6 @@
tests/cases/compiler/noImplicitAnyIndexing.ts(12,37): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
tests/cases/compiler/noImplicitAnyIndexing.ts(19,9): error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type '{}'.
Property 'hi' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(22,9): error TS7053: Element implicitly has an 'any' type because expression of type '10' can't be used to index type '{}'.
Property '10' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(19,9): error TS2339: Property 'hi' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(22,9): error TS2339: Property '10' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{}'.
@ -29,14 +27,12 @@ tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7053: Element impl
// Should report an implicit 'any'.
var x = {}["hi"];
~~~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type '{}'.
!!! error TS7053: Property 'hi' does not exist on type '{}'.
!!! error TS2339: Property 'hi' does not exist on type '{}'.
// Should report an implicit 'any'.
var y = {}[10];
~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type '10' can't be used to index type '{}'.
!!! error TS7053: Property '10' does not exist on type '{}'.
!!! error TS2339: Property '10' does not exist on type '{}'.
var hi: any = "hi";

View file

@ -39,15 +39,15 @@ var strRepresentation4 = MyEmusEnum["emu"];
// Should report an implicit 'any'.
var x = {}["hi"];
>x : any
>{}["hi"] : any
>x : undefined
>{}["hi"] : undefined
>{} : {}
>"hi" : "hi"
// Should report an implicit 'any'.
var y = {}[10];
>y : any
>{}[10] : any
>y : undefined
>{}[10] : undefined
>{} : {}
>10 : 10

View file

@ -0,0 +1,56 @@
tests/cases/compiler/noImplicitAnyIndexingSuppressed.ts(19,9): error TS2339: Property 'hi' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexingSuppressed.ts(22,9): error TS2339: Property '10' does not exist on type '{}'.
==== tests/cases/compiler/noImplicitAnyIndexingSuppressed.ts (2 errors) ====
enum MyEmusEnum {
emu
}
// Should be okay; should be a string.
var strRepresentation1 = MyEmusEnum[0]
// Should be okay; should be a string.
var strRepresentation2 = MyEmusEnum[MyEmusEnum.emu]
// Should be okay, as we suppress implicit 'any' property access checks
var strRepresentation3 = MyEmusEnum["monehh"];
// Should be okay; should be a MyEmusEnum
var strRepresentation4 = MyEmusEnum["emu"];
// Should be okay, as we suppress implicit 'any' property access checks
var x = {}["hi"];
~~~~~~~~
!!! error TS2339: Property 'hi' does not exist on type '{}'.
// Should be okay, as we suppress implicit 'any' property access checks
var y = {}[10];
~~~~~~
!!! error TS2339: Property '10' does not exist on type '{}'.
var hi: any = "hi";
var emptyObj = {};
// Should be okay, as we suppress implicit 'any' property access checks
var z1 = emptyObj[hi];
var z2 = (<any>emptyObj)[hi];
interface MyMap<T> {
[key: string]: T;
}
var m: MyMap<number> = {
"0": 0,
"1": 1,
"2": 2,
"Okay that's enough for today.": NaN
};
var mResult1 = m[MyEmusEnum.emu];
var mResult2 = m[MyEmusEnum[MyEmusEnum.emu]];
var mResult3 = m[hi];

View file

@ -24,8 +24,8 @@ var strRepresentation2 = MyEmusEnum[MyEmusEnum.emu]
// Should be okay, as we suppress implicit 'any' property access checks
var strRepresentation3 = MyEmusEnum["monehh"];
>strRepresentation3 : error
>MyEmusEnum["monehh"] : error
>strRepresentation3 : any
>MyEmusEnum["monehh"] : any
>MyEmusEnum : typeof MyEmusEnum
>"monehh" : "monehh"
@ -39,15 +39,15 @@ var strRepresentation4 = MyEmusEnum["emu"];
// Should be okay, as we suppress implicit 'any' property access checks
var x = {}["hi"];
>x : error
>{}["hi"] : error
>x : undefined
>{}["hi"] : undefined
>{} : {}
>"hi" : "hi"
// Should be okay, as we suppress implicit 'any' property access checks
var y = {}[10];
>y : error
>{}[10] : error
>y : undefined
>{}[10] : undefined
>{} : {}
>10 : 10
@ -61,8 +61,8 @@ var emptyObj = {};
// Should be okay, as we suppress implicit 'any' property access checks
var z1 = emptyObj[hi];
>z1 : error
>emptyObj[hi] : error
>z1 : any
>emptyObj[hi] : any
>emptyObj : {}
>hi : any

View file

@ -1,5 +1,4 @@
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(1,9): error TS7053: Element implicitly has an 'any' type because expression of type '"hello"' can't be used to index type '{}'.
Property 'hello' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(1,9): error TS2339: Property 'hello' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(7,1): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; }' has no index signature. Did you mean to call 'c.get'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(8,13): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; }' has no index signature. Did you mean to call 'c.get'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(13,13): error TS7053: Element implicitly has an 'any' type because expression of type '"hello"' can't be used to index type '{ set: (key: string) => string; }'.
@ -16,11 +15,11 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,3): error TS7052:
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(43,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; }' has no index signature. Did you mean to call 'e.set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(44,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; }' has no index signature. Did you mean to call 'e.set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(45,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; }' has no index signature. Did you mean to call 'e.set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(49,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'get'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(49,3): error TS2339: Property 'hello' does not exist on type '{ get: (key: string) => string; set: (key: string, value: string) => void; }'.
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(50,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(51,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(52,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(56,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'get'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(56,3): error TS2339: Property 'hello' does not exist on type '{ get: (key: string) => string; set: (key: string, value: string) => void; }'.
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(57,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(58,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(59,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
@ -41,8 +40,7 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(99,1): error TS7052:
==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (31 errors) ====
var a = {}["hello"];
~~~~~~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"hello"' can't be used to index type '{}'.
!!! error TS7053: Property 'hello' does not exist on type '{}'.
!!! error TS2339: Property 'hello' does not exist on type '{}'.
var b: string = { '': 'foo' }[''];
var c = {
@ -123,7 +121,7 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(99,1): error TS7052:
{
({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'get'?
!!! error TS2339: Property 'hello' does not exist on type '{ get: (key: string) => string; set: (key: string, value: string) => void; }'.
({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified';
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?
@ -138,7 +136,7 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(99,1): error TS7052:
{
({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'get'?
!!! error TS2339: Property 'hello' does not exist on type '{ get: (key: string) => string; set: (key: string, value: string) => void; }'.
({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] = 'modified';
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => void; }' has no index signature. Did you mean to call 'set'?

View file

@ -1,7 +1,7 @@
=== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts ===
var a = {}["hello"];
>a : any
>{}["hello"] : any
>a : undefined
>{}["hello"] : undefined
>{} : {}
>"hello" : "hello"
@ -188,7 +188,7 @@ const bar = d['hello'];
{
({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'];
>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] : any
>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] : undefined
>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} }) : { get: (key: string) => string; set: (key: string, value: string) => void; }
>{ get: (key: string) => 'hello', set: (key: string, value: string) => {} } : { get: (key: string) => string; set: (key: string, value: string) => void; }
>get : (key: string) => string
@ -251,7 +251,7 @@ const bar = d['hello'];
{
({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'];
>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] : any
>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] : undefined
>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo : { get: (key: string) => string; set: (key: string, value: string) => void; }
>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }) : { foo: { get: (key: string) => string; set: (key: string, value: string) => void; }; }
>{ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } } : { foo: { get: (key: string) => string; set: (key: string, value: string) => void; }; }

View file

@ -0,0 +1,55 @@
// @strict: true
function foo (id: string) {
return {
a: 1,
b: "",
c: true
}[id]
}
function bar (id: 'a' | 'b') {
return {
a: 1,
b: "",
c: false
}[id]
}
function baz (id: '1' | '2') {
return {
1: 1,
2: "",
3: false
}[id]
}
function qux (id: 1 | 2) {
return {
1: 1,
2: "",
3: false
}[id]
}
function quux (id: 'a' | 'b' | 'z') {
return {
a: 1,
b: "",
c: false
}[id]
}
function corge(id: string) {
return ({
a: 123,
b: ""
} as Record<string, number | string>)[id]
}
function grault(id: string) {
return ({
a: 123,
b: ""
} as { [k: string]: string | number})[id]
}