From b50b9e0fd2602815e1c23480ca6cb577896e6758 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 19 Nov 2019 13:00:24 -0800 Subject: [PATCH] Fix check for `get`/`set` recommendation (#34885) * Added more tests. * Accepted baselines. * Work better with any parameter type. * Accepted baselines. * Use the actual indexed expression. * Add tests that exhibit bad stringification. * Accepted baselines. * Short-circuit stringification on 'undefined'. * Accepted baselines. * Remove space. * Accepted baselines. --- src/compiler/checker.ts | 12 +- src/compiler/diagnosticMessages.json | 2 +- src/compiler/utilities.ts | 7 +- src/services/navigationBar.ts | 1 + ...mplicitAnyStringIndexerOnObject.errors.txt | 157 +++++++-- .../noImplicitAnyStringIndexerOnObject.js | 116 ++++++- ...noImplicitAnyStringIndexerOnObject.symbols | 249 +++++++++++--- .../noImplicitAnyStringIndexerOnObject.types | 303 +++++++++++++++++- .../noImplicitAnyStringIndexerOnObject.ts | 65 +++- 9 files changed, 798 insertions(+), 114 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0931b235552..e7befc3a4dc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11995,7 +11995,7 @@ namespace ts { } } else { - const suggestion = getSuggestionForNonexistentIndexSignature(objectType, accessExpression); + const suggestion = getSuggestionForNonexistentIndexSignature(objectType, accessExpression, indexType); if (suggestion !== undefined) { error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, typeToString(objectType), suggestion); } @@ -23154,15 +23154,13 @@ namespace ts { return suggestion && symbolName(suggestion); } - function getSuggestionForNonexistentIndexSignature(objectType: Type, expr: ElementAccessExpression): string | undefined { + function getSuggestionForNonexistentIndexSignature(objectType: Type, expr: ElementAccessExpression, keyedType: Type): string | undefined { // check if object type has setter or getter - const hasProp = (name: "set" | "get", argCount = 1) => { + function hasProp(name: "set" | "get") { const prop = getPropertyOfObjectType(objectType, <__String>name); if (prop) { const s = getSingleCallSignature(getTypeOfSymbol(prop)); - if (s && getMinArgumentCount(s) === argCount && typeToString(getTypeAtPosition(s, 0)) === "string") { - return true; - } + return !!s && getMinArgumentCount(s) >= 1 && isTypeAssignableTo(keyedType, getTypeAtPosition(s, 0)); } return false; }; @@ -23172,7 +23170,7 @@ namespace ts { return undefined; } - let suggestion = tryGetPropertyAccessOrIdentifierToString(expr); + let suggestion = tryGetPropertyAccessOrIdentifierToString(expr.expression); if (suggestion === undefined) { suggestion = suggestedMethod; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d93b725c01b..02e852869cd 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4518,7 +4518,7 @@ "category": "Error", "code": 7051 }, - "Element implicitly has an 'any' type because type '{0}' has no index signature. Did you mean to call '{1}' ?": { + "Element implicitly has an 'any' type because type '{0}' has no index signature. Did you mean to call '{1}'?": { "category": "Error", "code": 7052 }, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 935c528ebe2..5e40405817e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4244,9 +4244,12 @@ namespace ts { export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined { if (isPropertyAccessExpression(expr)) { - return tryGetPropertyAccessOrIdentifierToString(expr.expression) + "." + expr.name; + const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression); + if (baseStr !== undefined) { + return baseStr + "." + expr.name; + } } - if (isIdentifier(expr)) { + else if (isIdentifier(expr)) { return unescapeLeadingUnderscores(expr.escapedText); } return undefined; diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 49310ef827f..ba369f975fb 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -901,6 +901,7 @@ namespace ts.NavigationBar { return ""; } + // See also 'tryGetPropertyAccessOrIdentifierToString' function getCalledExpressionName(expr: Expression): string | undefined { if (isIdentifier(expr)) { return expr.text; diff --git a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.errors.txt b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.errors.txt index 0d2fc1b860e..729c8236abd 100644 --- a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.errors.txt +++ b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.errors.txt @@ -1,25 +1,43 @@ 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(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 '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 'get' ? +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; }'. Property 'hello' does not exist on type '{ set: (key: string) => string; }'. -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(19,1): error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(20,1): error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(21,1): error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(26,1): error TS7053: Element implicitly has an 'any' type because expression of type '"a" | "b" | "c"' can't be used to index type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(20,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.get'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(21,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(22,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(23,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(31,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.get'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(32,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(33,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(34,3): error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,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.get'? +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(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(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'? +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(65,1): error TS7053: Element implicitly has an 'any' type because expression of type '"a" | "b" | "c"' can't be used to index type '{ a: number; }'. Property 'b' does not exist on type '{ a: number; }'. -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(30,1): error TS7053: Element implicitly has an 'any' type because expression of type '"c"' can't be used to index type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(69,1): error TS7053: Element implicitly has an 'any' type because expression of type '"c"' can't be used to index type '{ a: number; }'. Property 'c' does not exist on type '{ a: number; }'. -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(33,1): error TS7053: Element implicitly has an 'any' type because expression of type 'unique symbol' can't be used to index type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(72,1): error TS7053: Element implicitly has an 'any' type because expression of type 'unique symbol' can't be used to index type '{ a: number; }'. Property '[sym]' does not exist on type '{ a: number; }'. -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(37,1): error TS7053: Element implicitly has an 'any' type because expression of type 'NumEnum' can't be used to index type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(76,1): error TS7053: Element implicitly has an 'any' type because expression of type 'NumEnum' can't be used to index type '{ a: number; }'. Property '[NumEnum.a]' does not exist on type '{ a: number; }'. -tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,1): error TS7053: Element implicitly has an 'any' type because expression of type 'StrEnum' can't be used to index type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(81,1): error TS7053: Element implicitly has an 'any' type because expression of type 'StrEnum' can't be used to index type '{ a: number; }'. Property '[StrEnum.b]' does not exist on type '{ a: number; }'. +tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(93,5): error TS2538: Type 'Dog' cannot be used as an index type. -==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (12 errors) ==== +==== tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts (30 errors) ==== var a = {}["hello"]; ~~~~~~~~~~~ !!! error TS7053: Element implicitly has an 'any' type because expression of type '"hello"' can't be used to index type '{}'. @@ -31,10 +49,10 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,1): error TS7053: }; c['hello']; ~~~~~~~~~~ -!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; }' has no index signature. Did you mean to call 'get' ? +!!! 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'? const foo = c['hello']; ~~~~~~~~~~ -!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; }' has no index signature. Did you mean to call 'get' ? +!!! 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'? var d = { set: (key: string) => 'foobar' @@ -44,19 +62,92 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,1): error TS7053: !!! error TS7053: Element implicitly has an 'any' type because expression of type '"hello"' can't be used to index type '{ set: (key: string) => string; }'. !!! error TS7053: Property 'hello' does not exist on type '{ set: (key: string) => string; }'. - var e = { - set: (key: string) => 'foobar', - get: (key: string) => 'foobar' - }; - e['hello'] = 'modified'; - ~~~~~~~~~~ -!!! error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? - e['hello'] += 1; - ~~~~~~~~~~ -!!! error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? - e['hello'] ++; - ~~~~~~~~~~ -!!! error TS7052: Element implicitly has an 'any' type because type '{ set: (key: string) => string; get: (key: string) => string; }' has no index signature. Did you mean to call 'set' ? + { + let e = { + get: (key: string) => 'foobar', + set: (key: string) => 'foobar' + }; + e['hello']; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.get'? + e['hello'] = 'modified'; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? + e['hello'] += 1; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? + e['hello'] ++; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string) => string; }' has no index signature. Did you mean to call 'e.set'? + } + + { + let e = { + get: (key: string) => 'foobar', + set: (key: string, value: string) => 'foobar' + }; + e['hello']; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.get'? + e['hello'] = 'modified'; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? + e['hello'] += 1; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? + e['hello'] ++; + ~~~~~~~~~~ +!!! error TS7052: Element implicitly has an 'any' type because type '{ get: (key: string) => string; set: (key: string, value: string) => string; }' has no index signature. Did you mean to call 'e.set'? + } + + { + let e = { + get: (key: "hello" | "world") => 'foobar', + set: (key: "hello" | "world", value: string) => 'foobar' + }; + e['hello']; + ~~~~~~~~~~ +!!! 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.get'? + e['hello'] = 'modified'; + ~~~~~~~~~~ +!!! 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'? + e['hello'] += 1; + ~~~~~~~~~~ +!!! 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'? + e['hello'] ++; + ~~~~~~~~~~ +!!! 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'? + } + + { + ({ 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'? + ({ 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'? + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! 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'? + ({ 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 'set'? + } + + { + ({ 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'? + ({ 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'? + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! 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'? + ({ 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 'set'? + } const o = { a: 0 }; @@ -93,4 +184,18 @@ tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts(42,1): error TS7053: ~~~~~~~~~~~~~ !!! error TS7053: Element implicitly has an 'any' type because expression of type 'StrEnum' can't be used to index type '{ a: number; }'. !!! error TS7053: Property '[StrEnum.b]' does not exist on type '{ a: number; }'. + + + interface MyMap { + get(key: K): T; + set(key: K, value: T): void; + } + + interface Dog { bark(): void; } + let rover: Dog = { bark() {} }; + + declare let map: MyMap; + map[rover] = "Rover"; + ~~~~~ +!!! error TS2538: Type 'Dog' cannot be used as an index type. \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.js b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.js index 572d9f38caf..aade38856d0 100644 --- a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.js +++ b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.js @@ -13,13 +13,52 @@ var d = { }; const bar = d['hello']; -var e = { - set: (key: string) => 'foobar', - get: (key: string) => 'foobar' -}; -e['hello'] = 'modified'; -e['hello'] += 1; -e['hello'] ++; +{ + let e = { + get: (key: string) => 'foobar', + set: (key: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + let e = { + get: (key: string) => 'foobar', + set: (key: string, value: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + let e = { + get: (key: "hello" | "world") => 'foobar', + set: (key: "hello" | "world", value: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello']; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified'; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['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'] = 'modified'; + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1; + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] ++; +} const o = { a: 0 }; @@ -41,6 +80,18 @@ o[numEnumKey]; enum StrEnum { a = "a", b = "b" } let strEnumKey: StrEnum; o[strEnumKey]; + + +interface MyMap { + get(key: K): T; + set(key: K, value: T): void; +} + +interface Dog { bark(): void; } +let rover: Dog = { bark() {} }; + +declare let map: MyMap; +map[rover] = "Rover"; //// [noImplicitAnyStringIndexerOnObject.js] @@ -55,13 +106,48 @@ var d = { set: function (key) { return 'foobar'; } }; var bar = d['hello']; -var e = { - set: function (key) { return 'foobar'; }, - get: function (key) { return 'foobar'; } -}; -e['hello'] = 'modified'; -e['hello'] += 1; -e['hello']++; +{ + var e = { + get: function (key) { return 'foobar'; }, + set: function (key) { return 'foobar'; } + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello']++; +} +{ + var e = { + get: function (key) { return 'foobar'; }, + set: function (key, value) { return 'foobar'; } + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello']++; +} +{ + var e = { + get: function (key) { return 'foobar'; }, + set: function (key, value) { return 'foobar'; } + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello']++; +} +{ + ({ get: function (key) { return 'hello'; }, set: function (key, value) { } })['hello']; + ({ get: function (key) { return 'hello'; }, set: function (key, value) { } })['hello'] = 'modified'; + ({ get: function (key) { return 'hello'; }, set: function (key, value) { } })['hello'] += 1; + ({ get: function (key) { return 'hello'; }, set: function (key, value) { } })['hello']++; +} +{ + ({ foo: { get: function (key) { return 'hello'; }, set: function (key, value) { } } }).foo['hello']; + ({ foo: { get: function (key) { return 'hello'; }, set: function (key, value) { } } }).foo['hello'] = 'modified'; + ({ foo: { get: function (key) { return 'hello'; }, set: function (key, value) { } } }).foo['hello'] += 1; + ({ foo: { get: function (key) { return 'hello'; }, set: function (key, value) { } } }).foo['hello']++; +} var o = { a: 0 }; o[k]; o[k2]; @@ -80,3 +166,5 @@ var StrEnum; })(StrEnum || (StrEnum = {})); var strEnumKey; o[strEnumKey]; +var rover = { bark: function () { } }; +map[rover] = "Rover"; diff --git a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.symbols b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.symbols index 307144b8563..17361623965 100644 --- a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.symbols +++ b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.symbols @@ -34,77 +34,246 @@ const bar = d['hello']; >bar : Symbol(bar, Decl(noImplicitAnyStringIndexerOnObject.ts, 12, 5)) >d : Symbol(d, Decl(noImplicitAnyStringIndexerOnObject.ts, 9, 3)) -var e = { ->e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 14, 3)) +{ + let e = { +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 5)) - set: (key: string) => 'foobar', ->set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 14, 9)) ->key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 8)) + get: (key: string) => 'foobar', +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 16, 10)) - get: (key: string) => 'foobar' ->get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 33)) ->key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 16, 8)) + set: (key: string) => 'foobar' +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 16, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 17, 10)) -}; -e['hello'] = 'modified'; ->e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 14, 3)) + }; + e['hello']; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 5)) -e['hello'] += 1; ->e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 14, 3)) + e['hello'] = 'modified'; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 5)) -e['hello'] ++; ->e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 14, 3)) + e['hello'] += 1; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 5)) + + e['hello'] ++; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 15, 5)) +} + +{ + let e = { +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 5)) + + get: (key: string) => 'foobar', +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 27, 10)) + + set: (key: string, value: string) => 'foobar' +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 27, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 28, 10)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 28, 22)) + + }; + e['hello']; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 5)) + + e['hello'] = 'modified'; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 5)) + + e['hello'] += 1; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 5)) + + e['hello'] ++; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 26, 5)) +} + +{ + let e = { +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 5)) + + get: (key: "hello" | "world") => 'foobar', +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 38, 10)) + + set: (key: "hello" | "world", value: string) => 'foobar' +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 38, 46)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 39, 10)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 39, 33)) + + }; + e['hello']; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 5)) + + e['hello'] = 'modified'; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 5)) + + e['hello'] += 1; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 5)) + + e['hello'] ++; +>e : Symbol(e, Decl(noImplicitAnyStringIndexerOnObject.ts, 37, 5)) +} + +{ + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello']; +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 48, 4)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 48, 11)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 48, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 48, 42)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 48, 54)) + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified'; +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 49, 4)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 49, 11)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 49, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 49, 42)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 49, 54)) + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1; +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 50, 4)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 50, 11)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 50, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 50, 42)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 50, 54)) + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] ++; +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 51, 4)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 51, 11)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 51, 35)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 51, 42)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 51, 54)) +} + +{ + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello']; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 4)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 4)) +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 18)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 42)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 49)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 61)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 55, 4)) + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] = 'modified'; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 4)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 4)) +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 18)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 42)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 49)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 61)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 56, 4)) + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 4)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 4)) +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 18)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 42)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 49)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 61)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 57, 4)) + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] ++; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 4)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 4)) +>get : Symbol(get, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 11)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 18)) +>set : Symbol(set, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 42)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 49)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 61)) +>foo : Symbol(foo, Decl(noImplicitAnyStringIndexerOnObject.ts, 58, 4)) +} const o = { a: 0 }; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->a : Symbol(a, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 11)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>a : Symbol(a, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 11)) declare const k: "a" | "b" | "c"; ->k : Symbol(k, Decl(noImplicitAnyStringIndexerOnObject.ts, 24, 13)) +>k : Symbol(k, Decl(noImplicitAnyStringIndexerOnObject.ts, 63, 13)) o[k]; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->k : Symbol(k, Decl(noImplicitAnyStringIndexerOnObject.ts, 24, 13)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>k : Symbol(k, Decl(noImplicitAnyStringIndexerOnObject.ts, 63, 13)) declare const k2: "c"; ->k2 : Symbol(k2, Decl(noImplicitAnyStringIndexerOnObject.ts, 28, 13)) +>k2 : Symbol(k2, Decl(noImplicitAnyStringIndexerOnObject.ts, 67, 13)) o[k2]; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->k2 : Symbol(k2, Decl(noImplicitAnyStringIndexerOnObject.ts, 28, 13)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>k2 : Symbol(k2, Decl(noImplicitAnyStringIndexerOnObject.ts, 67, 13)) declare const sym : unique symbol; ->sym : Symbol(sym, Decl(noImplicitAnyStringIndexerOnObject.ts, 31, 13)) +>sym : Symbol(sym, Decl(noImplicitAnyStringIndexerOnObject.ts, 70, 13)) o[sym]; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->sym : Symbol(sym, Decl(noImplicitAnyStringIndexerOnObject.ts, 31, 13)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>sym : Symbol(sym, Decl(noImplicitAnyStringIndexerOnObject.ts, 70, 13)) enum NumEnum { a, b } ->NumEnum : Symbol(NumEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 32, 7)) ->a : Symbol(NumEnum.a, Decl(noImplicitAnyStringIndexerOnObject.ts, 34, 14)) ->b : Symbol(NumEnum.b, Decl(noImplicitAnyStringIndexerOnObject.ts, 34, 17)) +>NumEnum : Symbol(NumEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 71, 7)) +>a : Symbol(NumEnum.a, Decl(noImplicitAnyStringIndexerOnObject.ts, 73, 14)) +>b : Symbol(NumEnum.b, Decl(noImplicitAnyStringIndexerOnObject.ts, 73, 17)) let numEnumKey: NumEnum; ->numEnumKey : Symbol(numEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 35, 3)) ->NumEnum : Symbol(NumEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 32, 7)) +>numEnumKey : Symbol(numEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 74, 3)) +>NumEnum : Symbol(NumEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 71, 7)) o[numEnumKey]; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->numEnumKey : Symbol(numEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 35, 3)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>numEnumKey : Symbol(numEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 74, 3)) enum StrEnum { a = "a", b = "b" } ->StrEnum : Symbol(StrEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 36, 14)) ->a : Symbol(StrEnum.a, Decl(noImplicitAnyStringIndexerOnObject.ts, 39, 14)) ->b : Symbol(StrEnum.b, Decl(noImplicitAnyStringIndexerOnObject.ts, 39, 23)) +>StrEnum : Symbol(StrEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 75, 14)) +>a : Symbol(StrEnum.a, Decl(noImplicitAnyStringIndexerOnObject.ts, 78, 14)) +>b : Symbol(StrEnum.b, Decl(noImplicitAnyStringIndexerOnObject.ts, 78, 23)) let strEnumKey: StrEnum; ->strEnumKey : Symbol(strEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 40, 3)) ->StrEnum : Symbol(StrEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 36, 14)) +>strEnumKey : Symbol(strEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 79, 3)) +>StrEnum : Symbol(StrEnum, Decl(noImplicitAnyStringIndexerOnObject.ts, 75, 14)) o[strEnumKey]; ->o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 22, 5)) ->strEnumKey : Symbol(strEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 40, 3)) +>o : Symbol(o, Decl(noImplicitAnyStringIndexerOnObject.ts, 61, 5)) +>strEnumKey : Symbol(strEnumKey, Decl(noImplicitAnyStringIndexerOnObject.ts, 79, 3)) + + +interface MyMap { +>MyMap : Symbol(MyMap, Decl(noImplicitAnyStringIndexerOnObject.ts, 80, 14)) +>K : Symbol(K, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 16)) +>T : Symbol(T, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 18)) + + get(key: K): T; +>get : Symbol(MyMap.get, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 23)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 84, 6)) +>K : Symbol(K, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 16)) +>T : Symbol(T, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 18)) + + set(key: K, value: T): void; +>set : Symbol(MyMap.set, Decl(noImplicitAnyStringIndexerOnObject.ts, 84, 17)) +>key : Symbol(key, Decl(noImplicitAnyStringIndexerOnObject.ts, 85, 6)) +>K : Symbol(K, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 16)) +>value : Symbol(value, Decl(noImplicitAnyStringIndexerOnObject.ts, 85, 13)) +>T : Symbol(T, Decl(noImplicitAnyStringIndexerOnObject.ts, 83, 18)) +} + +interface Dog { bark(): void; } +>Dog : Symbol(Dog, Decl(noImplicitAnyStringIndexerOnObject.ts, 86, 1)) +>bark : Symbol(Dog.bark, Decl(noImplicitAnyStringIndexerOnObject.ts, 88, 15)) + +let rover: Dog = { bark() {} }; +>rover : Symbol(rover, Decl(noImplicitAnyStringIndexerOnObject.ts, 89, 3)) +>Dog : Symbol(Dog, Decl(noImplicitAnyStringIndexerOnObject.ts, 86, 1)) +>bark : Symbol(bark, Decl(noImplicitAnyStringIndexerOnObject.ts, 89, 18)) + +declare let map: MyMap; +>map : Symbol(map, Decl(noImplicitAnyStringIndexerOnObject.ts, 91, 11)) +>MyMap : Symbol(MyMap, Decl(noImplicitAnyStringIndexerOnObject.ts, 80, 14)) +>Dog : Symbol(Dog, Decl(noImplicitAnyStringIndexerOnObject.ts, 86, 1)) + +map[rover] = "Rover"; +>map : Symbol(map, Decl(noImplicitAnyStringIndexerOnObject.ts, 91, 11)) +>rover : Symbol(rover, Decl(noImplicitAnyStringIndexerOnObject.ts, 89, 3)) diff --git a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.types b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.types index fa74a192248..4a12805418b 100644 --- a/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.types +++ b/tests/baselines/reference/noImplicitAnyStringIndexerOnObject.types @@ -52,42 +52,281 @@ const bar = d['hello']; >d : { set: (key: string) => string; } >'hello' : "hello" -var e = { ->e : { set: (key: string) => string; get: (key: string) => string; } ->{ set: (key: string) => 'foobar', get: (key: string) => 'foobar'} : { set: (key: string) => string; get: (key: string) => string; } +{ + let e = { +>e : { get: (key: string) => string; set: (key: string) => string; } +>{ get: (key: string) => 'foobar', set: (key: string) => 'foobar' } : { get: (key: string) => string; set: (key: string) => string; } - set: (key: string) => 'foobar', ->set : (key: string) => string ->(key: string) => 'foobar' : (key: string) => string ->key : string ->'foobar' : "foobar" - - get: (key: string) => 'foobar' + get: (key: string) => 'foobar', >get : (key: string) => string >(key: string) => 'foobar' : (key: string) => string >key : string >'foobar' : "foobar" -}; -e['hello'] = 'modified'; + set: (key: string) => 'foobar' +>set : (key: string) => string +>(key: string) => 'foobar' : (key: string) => string +>key : string +>'foobar' : "foobar" + + }; + e['hello']; +>e['hello'] : any +>e : { get: (key: string) => string; set: (key: string) => string; } +>'hello' : "hello" + + e['hello'] = 'modified'; >e['hello'] = 'modified' : "modified" >e['hello'] : any ->e : { set: (key: string) => string; get: (key: string) => string; } +>e : { get: (key: string) => string; set: (key: string) => string; } >'hello' : "hello" >'modified' : "modified" -e['hello'] += 1; + e['hello'] += 1; >e['hello'] += 1 : any >e['hello'] : any ->e : { set: (key: string) => string; get: (key: string) => string; } +>e : { get: (key: string) => string; set: (key: string) => string; } >'hello' : "hello" >1 : 1 -e['hello'] ++; + e['hello'] ++; >e['hello'] ++ : number >e['hello'] : any ->e : { set: (key: string) => string; get: (key: string) => string; } +>e : { get: (key: string) => string; set: (key: string) => string; } >'hello' : "hello" +} + +{ + let e = { +>e : { get: (key: string) => string; set: (key: string, value: string) => string; } +>{ get: (key: string) => 'foobar', set: (key: string, value: string) => 'foobar' } : { get: (key: string) => string; set: (key: string, value: string) => string; } + + get: (key: string) => 'foobar', +>get : (key: string) => string +>(key: string) => 'foobar' : (key: string) => string +>key : string +>'foobar' : "foobar" + + set: (key: string, value: string) => 'foobar' +>set : (key: string, value: string) => string +>(key: string, value: string) => 'foobar' : (key: string, value: string) => string +>key : string +>value : string +>'foobar' : "foobar" + + }; + e['hello']; +>e['hello'] : any +>e : { get: (key: string) => string; set: (key: string, value: string) => string; } +>'hello' : "hello" + + e['hello'] = 'modified'; +>e['hello'] = 'modified' : "modified" +>e['hello'] : any +>e : { get: (key: string) => string; set: (key: string, value: string) => string; } +>'hello' : "hello" +>'modified' : "modified" + + e['hello'] += 1; +>e['hello'] += 1 : any +>e['hello'] : any +>e : { get: (key: string) => string; set: (key: string, value: string) => string; } +>'hello' : "hello" +>1 : 1 + + e['hello'] ++; +>e['hello'] ++ : number +>e['hello'] : any +>e : { get: (key: string) => string; set: (key: string, value: string) => string; } +>'hello' : "hello" +} + +{ + let e = { +>e : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } +>{ get: (key: "hello" | "world") => 'foobar', set: (key: "hello" | "world", value: string) => 'foobar' } : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } + + get: (key: "hello" | "world") => 'foobar', +>get : (key: "hello" | "world") => string +>(key: "hello" | "world") => 'foobar' : (key: "hello" | "world") => string +>key : "hello" | "world" +>'foobar' : "foobar" + + set: (key: "hello" | "world", value: string) => 'foobar' +>set : (key: "hello" | "world", value: string) => string +>(key: "hello" | "world", value: string) => 'foobar' : (key: "hello" | "world", value: string) => string +>key : "hello" | "world" +>value : string +>'foobar' : "foobar" + + }; + e['hello']; +>e['hello'] : any +>e : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } +>'hello' : "hello" + + e['hello'] = 'modified'; +>e['hello'] = 'modified' : "modified" +>e['hello'] : any +>e : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } +>'hello' : "hello" +>'modified' : "modified" + + e['hello'] += 1; +>e['hello'] += 1 : any +>e['hello'] : any +>e : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } +>'hello' : "hello" +>1 : 1 + + e['hello'] ++; +>e['hello'] ++ : number +>e['hello'] : any +>e : { get: (key: "hello" | "world") => string; set: (key: "hello" | "world", value: string) => string; } +>'hello' : "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) => {} }) : { 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>'hello' : "hello" + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified'; +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified' : "modified" +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] : any +>({ 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>'hello' : "hello" +>'modified' : "modified" + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1; +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1 : any +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] : any +>({ 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>'hello' : "hello" +>1 : 1 + + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] ++; +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] ++ : number +>({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] : any +>({ 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>'hello' : "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 : { 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; }; } +>foo : { 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>foo : { get: (key: string) => string; set: (key: string, value: string) => void; } +>'hello' : "hello" + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] = 'modified'; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] = 'modified' : "modified" +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] : any +>({ 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; }; } +>foo : { 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>foo : { get: (key: string) => string; set: (key: string, value: string) => void; } +>'hello' : "hello" +>'modified' : "modified" + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1 : any +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] : any +>({ 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; }; } +>foo : { 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>foo : { get: (key: string) => string; set: (key: string, value: string) => void; } +>'hello' : "hello" +>1 : 1 + + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] ++; +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] ++ : number +>({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] : any +>({ 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; }; } +>foo : { 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 +>(key: string) => 'hello' : (key: string) => string +>key : string +>'hello' : "hello" +>set : (key: string, value: string) => void +>(key: string, value: string) => {} : (key: string, value: string) => void +>key : string +>value : string +>foo : { get: (key: string) => string; set: (key: string, value: string) => void; } +>'hello' : "hello" +} const o = { a: 0 }; >o : { a: number; } @@ -149,3 +388,33 @@ o[strEnumKey]; >o : { a: number; } >strEnumKey : StrEnum + +interface MyMap { + get(key: K): T; +>get : (key: K) => T +>key : K + + set(key: K, value: T): void; +>set : (key: K, value: T) => void +>key : K +>value : T +} + +interface Dog { bark(): void; } +>bark : () => void + +let rover: Dog = { bark() {} }; +>rover : Dog +>{ bark() {} } : { bark(): void; } +>bark : () => void + +declare let map: MyMap; +>map : MyMap + +map[rover] = "Rover"; +>map[rover] = "Rover" : "Rover" +>map[rover] : any +>map : MyMap +>rover : Dog +>"Rover" : "Rover" + diff --git a/tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts b/tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts index 5e1b9430088..a9b11e29034 100644 --- a/tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts +++ b/tests/cases/compiler/noImplicitAnyStringIndexerOnObject.ts @@ -14,13 +14,52 @@ var d = { }; const bar = d['hello']; -var e = { - set: (key: string) => 'foobar', - get: (key: string) => 'foobar' -}; -e['hello'] = 'modified'; -e['hello'] += 1; -e['hello'] ++; +{ + let e = { + get: (key: string) => 'foobar', + set: (key: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + let e = { + get: (key: string) => 'foobar', + set: (key: string, value: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + let e = { + get: (key: "hello" | "world") => 'foobar', + set: (key: "hello" | "world", value: string) => 'foobar' + }; + e['hello']; + e['hello'] = 'modified'; + e['hello'] += 1; + e['hello'] ++; +} + +{ + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello']; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] = 'modified'; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['hello'] += 1; + ({ get: (key: string) => 'hello', set: (key: string, value: string) => {} })['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'] = 'modified'; + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] += 1; + ({ foo: { get: (key: string) => 'hello', set: (key: string, value: string) => {} } }).foo['hello'] ++; +} const o = { a: 0 }; @@ -42,3 +81,15 @@ o[numEnumKey]; enum StrEnum { a = "a", b = "b" } let strEnumKey: StrEnum; o[strEnumKey]; + + +interface MyMap { + get(key: K): T; + set(key: K, value: T): void; +} + +interface Dog { bark(): void; } +let rover: Dog = { bark() {} }; + +declare let map: MyMap; +map[rover] = "Rover";