Skip to content

Commit 4759ade

Browse files
authored
Merge pull request #12671 from Microsoft/property-access-for-string-index-sigs
Property access for string index signatures
2 parents 71fcb29 + 34fa278 commit 4759ade

14 files changed

+123
-23
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12444,6 +12444,10 @@ namespace ts {
1244412444
}
1244512445
const prop = getPropertyOfType(apparentType, right.text);
1244612446
if (!prop) {
12447+
const stringIndexType = getIndexTypeOfType(apparentType, IndexKind.String);
12448+
if (stringIndexType) {
12449+
return stringIndexType;
12450+
}
1244712451
if (right.text && !checkAndReportErrorForExtendingInterface(node)) {
1244812452
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
1244912453
}

tests/baselines/reference/objectRest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ let computed2 = 'a';
4444
var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
4545
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
4646

47-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
47+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
4848

4949

5050
//// [objectRest.js]
@@ -89,6 +89,6 @@ var _g = computed, stillNotGreat = o[_g], _h = computed2, soSo = o[_h], o = __re
8989
(_j = computed, stillNotGreat = o[_j], _k = computed2, soSo = o[_k], o = __rest(o, [typeof _j === "symbol" ? _j : _j + "", typeof _k === "symbol" ? _k : _k + ""]));
9090
var noContextualType = (_a) => {
9191
var { aNumber = 12 } = _a, notEmptyObject = __rest(_a, ["aNumber"]);
92-
return aNumber + notEmptyObject['anythingGoes'];
92+
return aNumber + notEmptyObject.anythingGoes;
9393
};
9494
var _d, _f, _j, _k;

tests/baselines/reference/objectRest.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
191191
>o : Symbol(o, Decl(objectRest.ts, 0, 3), Decl(objectRest.ts, 42, 51))
192192
>o : Symbol(o, Decl(objectRest.ts, 0, 3), Decl(objectRest.ts, 42, 51))
193193

194-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
194+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
195195
>noContextualType : Symbol(noContextualType, Decl(objectRest.ts, 45, 3))
196196
>aNumber : Symbol(aNumber, Decl(objectRest.ts, 45, 25))
197197
>notEmptyObject : Symbol(notEmptyObject, Decl(objectRest.ts, 45, 39))

tests/baselines/reference/objectRest.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,15 @@ var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
217217
>o : { a: number; b: string; }
218218
>o : { a: number; b: string; }
219219

220-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
220+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
221221
>noContextualType : ({aNumber, ...notEmptyObject}: { [x: string]: any; aNumber?: number; }) => any
222-
>({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'] : ({aNumber, ...notEmptyObject}: { [x: string]: any; aNumber?: number; }) => any
222+
>({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes : ({aNumber, ...notEmptyObject}: { [x: string]: any; aNumber?: number; }) => any
223223
>aNumber : number
224224
>12 : 12
225225
>notEmptyObject : { [x: string]: any; }
226-
>aNumber + notEmptyObject['anythingGoes'] : any
226+
>aNumber + notEmptyObject.anythingGoes : any
227227
>aNumber : number
228-
>notEmptyObject['anythingGoes'] : any
228+
>notEmptyObject.anythingGoes : any
229229
>notEmptyObject : { [x: string]: any; }
230-
>'anythingGoes' : "anythingGoes"
230+
>anythingGoes : any
231231

tests/baselines/reference/objectRestNegative.errors.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(11,30): error TS7008: M
77
tests/cases/conformance/types/rest/objectRestNegative.ts(11,33): error TS7008: Member 'y' implicitly has an 'any' type.
88
tests/cases/conformance/types/rest/objectRestNegative.ts(12,17): error TS2700: Rest types may only be created from object types.
99
tests/cases/conformance/types/rest/objectRestNegative.ts(17,9): error TS2701: The target of an object rest assignment must be a variable or a property access.
10-
tests/cases/conformance/types/rest/objectRestNegative.ts(19,90): error TS2339: Property 'anythingGoes' does not exist on type '{ [x: string]: any; }'.
1110

1211

13-
==== tests/cases/conformance/types/rest/objectRestNegative.ts (8 errors) ====
12+
==== tests/cases/conformance/types/rest/objectRestNegative.ts (7 errors) ====
1413
let o = { a: 1, b: 'no' };
1514
var { ...mustBeLast, a } = o;
1615
~~~~~~~~~~
@@ -44,8 +43,4 @@ tests/cases/conformance/types/rest/objectRestNegative.ts(19,90): error TS2339: P
4443
({a, ...rest.b + rest.b} = o);
4544
~~~~~~~~~~~~~~~
4645
!!! error TS2701: The target of an object rest assignment must be a variable or a property access.
47-
48-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
49-
~~~~~~~~~~~~
50-
!!! error TS2339: Property 'anythingGoes' does not exist on type '{ [x: string]: any; }'.
5146

tests/baselines/reference/objectRestNegative.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ function generic<T extends { x, y }>(t: T) {
1616

1717
let rest: { b: string }
1818
({a, ...rest.b + rest.b} = o);
19-
20-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;
2119

2220

2321
//// [objectRestNegative.js]
@@ -44,7 +42,3 @@ function generic(t) {
4442
}
4543
var rest;
4644
(a = o.a, o, rest.b + rest.b = __rest(o, ["a"]));
47-
var noContextualType = function (_a) {
48-
var _b = _a.aNumber, aNumber = _b === void 0 ? 12 : _b, notEmptyObject = __rest(_a, ["aNumber"]);
49-
return aNumber + notEmptyObject.anythingGoes;
50-
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/conformance/expressions/propertyAccess/propertyAccessStringIndexSignature.ts(10,7): error TS2339: Property 'nope' does not exist on type 'Empty'.
2+
3+
4+
==== tests/cases/conformance/expressions/propertyAccess/propertyAccessStringIndexSignature.ts (1 errors) ====
5+
interface Flags { [name: string]: boolean };
6+
let flags: Flags;
7+
flags.b;
8+
flags.f;
9+
flags.isNotNecessarilyNeverFalse;
10+
flags['this is fine'];
11+
12+
interface Empty { }
13+
let empty: Empty;
14+
empty.nope;
15+
~~~~
16+
!!! error TS2339: Property 'nope' does not exist on type 'Empty'.
17+
empty["that's ok"];
18+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [propertyAccessStringIndexSignature.ts]
2+
interface Flags { [name: string]: boolean };
3+
let flags: Flags;
4+
flags.b;
5+
flags.f;
6+
flags.isNotNecessarilyNeverFalse;
7+
flags['this is fine'];
8+
9+
interface Empty { }
10+
let empty: Empty;
11+
empty.nope;
12+
empty["that's ok"];
13+
14+
15+
//// [propertyAccessStringIndexSignature.js]
16+
;
17+
var flags;
18+
flags.b;
19+
flags.f;
20+
flags.isNotNecessarilyNeverFalse;
21+
flags['this is fine'];
22+
var empty;
23+
empty.nope;
24+
empty["that's ok"];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/conformance/expressions/propertyAccess/propertyAccessStringIndexSignatureNoImplicitAny.ts(10,7): error TS2339: Property 'nope' does not exist on type 'Empty'.
2+
tests/cases/conformance/expressions/propertyAccess/propertyAccessStringIndexSignatureNoImplicitAny.ts(11,1): error TS7017: Element implicitly has an 'any' type because type 'Empty' has no index signature.
3+
4+
5+
==== tests/cases/conformance/expressions/propertyAccess/propertyAccessStringIndexSignatureNoImplicitAny.ts (2 errors) ====
6+
interface Flags { [name: string]: boolean }
7+
let flags: Flags;
8+
flags.b;
9+
flags.f;
10+
flags.isNotNecessarilyNeverFalse;
11+
flags['this is fine'];
12+
13+
interface Empty { }
14+
let empty: Empty;
15+
empty.nope;
16+
~~~~
17+
!!! error TS2339: Property 'nope' does not exist on type 'Empty'.
18+
empty["not allowed either"];
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS7017: Element implicitly has an 'any' type because type 'Empty' has no index signature.
21+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [propertyAccessStringIndexSignatureNoImplicitAny.ts]
2+
interface Flags { [name: string]: boolean }
3+
let flags: Flags;
4+
flags.b;
5+
flags.f;
6+
flags.isNotNecessarilyNeverFalse;
7+
flags['this is fine'];
8+
9+
interface Empty { }
10+
let empty: Empty;
11+
empty.nope;
12+
empty["not allowed either"];
13+
14+
15+
//// [propertyAccessStringIndexSignatureNoImplicitAny.js]
16+
var flags;
17+
flags.b;
18+
flags.f;
19+
flags.isNotNecessarilyNeverFalse;
20+
flags['this is fine'];
21+
var empty;
22+
empty.nope;
23+
empty["not allowed either"];
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
interface Flags { [name: string]: boolean };
2+
let flags: Flags;
3+
flags.b;
4+
flags.f;
5+
flags.isNotNecessarilyNeverFalse;
6+
flags['this is fine'];
7+
8+
interface Empty { }
9+
let empty: Empty;
10+
empty.nope;
11+
empty["that's ok"];
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @noImplicitAny: true
2+
interface Flags { [name: string]: boolean }
3+
let flags: Flags;
4+
flags.b;
5+
flags.f;
6+
flags.isNotNecessarilyNeverFalse;
7+
flags['this is fine'];
8+
9+
interface Empty { }
10+
let empty: Empty;
11+
empty.nope;
12+
empty["not allowed either"];

tests/cases/conformance/types/rest/objectRest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ let computed2 = 'a';
4444
var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
4545
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
4646

47-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject['anythingGoes'];
47+
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;

tests/cases/conformance/types/rest/objectRestNegative.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,3 @@ function generic<T extends { x, y }>(t: T) {
1616

1717
let rest: { b: string }
1818
({a, ...rest.b + rest.b} = o);
19-
20-
var noContextualType = ({ aNumber = 12, ...notEmptyObject }) => aNumber + notEmptyObject.anythingGoes;

0 commit comments

Comments
 (0)