Skip to content

Commit 0179d97

Browse files
authored
Merge pull request microsoft#23592 from Microsoft/improveIndexTypes
Support number and symbol named properties with keyof and mapped types
2 parents 645258c + c7f55be commit 0179d97

File tree

114 files changed

+879
-841
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+879
-841
lines changed

src/compiler/checker.ts

+226-194
Large diffs are not rendered by default.

src/compiler/commandLineParser.ts

+6
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,12 @@ namespace ts {
672672
category: Diagnostics.Advanced_Options,
673673
description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types,
674674
},
675+
{
676+
name: "keyofStringsOnly",
677+
type: "boolean",
678+
category: Diagnostics.Advanced_Options,
679+
description: Diagnostics.Resolve_keyof_to_string_valued_property_names_only_no_numbers_or_symbols,
680+
},
675681
{
676682
// A list of plugins to load in the language service
677683
name: "plugins",

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3526,6 +3526,10 @@
35263526
"category": "Message",
35273527
"code": 6194
35283528
},
3529+
"Resolve 'keyof' to string valued property names only (no numbers or symbols).": {
3530+
"category": "Message",
3531+
"code": 6195
3532+
},
35293533
"Variable '{0}' implicitly has an '{1}' type.": {
35303534
"category": "Error",
35313535
"code": 7005

src/compiler/types.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -3377,6 +3377,7 @@ namespace ts {
33773377
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
33783378
/* @internal */ parent?: Symbol; // Parent symbol
33793379
/* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
3380+
/* @internal */ nameType?: Type; // Type associated with a late-bound symbol
33803381
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
33813382
/* @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter.
33823383
/* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
@@ -3411,7 +3412,6 @@ namespace ts {
34113412
enumKind?: EnumKind; // Enum declaration classification
34123413
originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol`
34133414
lateSymbol?: Symbol; // Late-bound symbol for a computed property
3414-
nameType?: Type; // Type associate with a late-bound or mapped type property symbol's name
34153415
}
34163416

34173417
/* @internal */
@@ -3608,7 +3608,7 @@ namespace ts {
36083608
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
36093609
/* @internal */
36103610
Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol,
3611-
StringLike = String | StringLiteral | Index,
3611+
StringLike = String | StringLiteral,
36123612
NumberLike = Number | NumberLiteral | Enum,
36133613
BooleanLike = Boolean | BooleanLiteral,
36143614
EnumLike = Enum | EnumLiteral,
@@ -3765,7 +3765,7 @@ namespace ts {
37653765
/* @internal */
37663766
resolvedIndexType: IndexType;
37673767
/* @internal */
3768-
resolvedDeclaredIndexType: IndexType;
3768+
resolvedStringIndexType: IndexType;
37693769
/* @internal */
37703770
resolvedBaseConstraint: Type;
37713771
/* @internal */
@@ -3854,7 +3854,7 @@ namespace ts {
38543854
/* @internal */
38553855
resolvedIndexType?: IndexType;
38563856
/* @internal */
3857-
resolvedDeclaredIndexType?: IndexType;
3857+
resolvedStringIndexType?: IndexType;
38583858
}
38593859

38603860
// Type parameters (TypeFlags.TypeParameter)
@@ -3886,9 +3886,9 @@ namespace ts {
38863886

38873887
// keyof T types (TypeFlags.Index)
38883888
export interface IndexType extends InstantiableType {
3889-
/* @internal */
3890-
isDeclaredType?: boolean;
38913889
type: InstantiableType | UnionOrIntersectionType;
3890+
/* @internal */
3891+
stringsOnly: boolean;
38923892
}
38933893

38943894
export interface ConditionalRoot {
@@ -4047,10 +4047,10 @@ namespace ts {
40474047

40484048
/* @internal */
40494049
export interface WideningContext {
4050-
parent?: WideningContext; // Parent context
4051-
propertyName?: __String; // Name of property in parent
4052-
siblings?: Type[]; // Types of siblings
4053-
resolvedPropertyNames?: __String[]; // Property names occurring in sibling object literals
4050+
parent?: WideningContext; // Parent context
4051+
propertyName?: __String; // Name of property in parent
4052+
siblings?: Type[]; // Types of siblings
4053+
resolvedProperties?: Symbol[]; // Properties occurring in sibling object literals
40544054
}
40554055

40564056
/* @internal */
@@ -4165,6 +4165,7 @@ namespace ts {
41654165
inlineSources?: boolean;
41664166
isolatedModules?: boolean;
41674167
jsx?: JsxEmit;
4168+
keyofStringsOnly?: boolean;
41684169
lib?: string[];
41694170
/*@internal*/listEmittedFiles?: boolean;
41704171
/*@internal*/listFiles?: boolean;

src/lib/es2015.core.d.ts

-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
declare type PropertyKey = string | number | symbol;
2-
31
interface Array<T> {
42
/**
53
* Returns the value of the first element in the array where predicate is true, and undefined
@@ -258,20 +256,6 @@ interface NumberConstructor {
258256
parseInt(string: string, radix?: number): number;
259257
}
260258

261-
interface Object {
262-
/**
263-
* Determines whether an object has a property with the specified name.
264-
* @param v A property name.
265-
*/
266-
hasOwnProperty(v: PropertyKey): boolean;
267-
268-
/**
269-
* Determines whether a specified property is enumerable.
270-
* @param v A property name.
271-
*/
272-
propertyIsEnumerable(v: PropertyKey): boolean;
273-
}
274-
275259
interface ObjectConstructor {
276260
/**
277261
* Copy the values of all of the enumerable own properties from one or more source objects to a
@@ -327,25 +311,6 @@ interface ObjectConstructor {
327311
* @param proto The value of the new prototype or null.
328312
*/
329313
setPrototypeOf(o: any, proto: object | null): any;
330-
331-
/**
332-
* Gets the own property descriptor of the specified object.
333-
* An own property descriptor is one that is defined directly on the object and is not
334-
* inherited from the object's prototype.
335-
* @param o Object that contains the property.
336-
* @param p Name of the property.
337-
*/
338-
getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor | undefined;
339-
340-
/**
341-
* Adds a property to an object, or modifies attributes of an existing property.
342-
* @param o Object on which to add or modify the property. This can be a native JavaScript
343-
* object (that is, a user-defined object or a built in object) or a DOM object.
344-
* @param p The property name.
345-
* @param attributes Descriptor for the property. It can be for a data property or an accessor
346-
* property.
347-
*/
348-
defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any;
349314
}
350315

351316
interface ReadonlyArray<T> {

src/lib/es5.d.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ declare function escape(string: string): string;
7474
*/
7575
declare function unescape(string: string): string;
7676

77+
declare type PropertyKey = string | number | symbol;
78+
7779
interface PropertyDescriptor {
7880
configurable?: boolean;
7981
enumerable?: boolean;
@@ -104,7 +106,7 @@ interface Object {
104106
* Determines whether an object has a property with the specified name.
105107
* @param v A property name.
106108
*/
107-
hasOwnProperty(v: string): boolean;
109+
hasOwnProperty(v: PropertyKey): boolean;
108110

109111
/**
110112
* Determines whether an object exists in another object's prototype chain.
@@ -116,7 +118,7 @@ interface Object {
116118
* Determines whether a specified property is enumerable.
117119
* @param v A property name.
118120
*/
119-
propertyIsEnumerable(v: string): boolean;
121+
propertyIsEnumerable(v: PropertyKey): boolean;
120122
}
121123

122124
interface ObjectConstructor {
@@ -139,7 +141,7 @@ interface ObjectConstructor {
139141
* @param o Object that contains the property.
140142
* @param p Name of the property.
141143
*/
142-
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor | undefined;
144+
getOwnPropertyDescriptor(o: any, p: PropertyKey): PropertyDescriptor | undefined;
143145

144146
/**
145147
* Returns the names of the own properties of an object. The own properties of an object are those that are defined directly
@@ -167,7 +169,7 @@ interface ObjectConstructor {
167169
* @param p The property name.
168170
* @param attributes Descriptor for the property. It can be for a data property or an accessor property.
169171
*/
170-
defineProperty(o: any, p: string, attributes: PropertyDescriptor & ThisType<any>): any;
172+
defineProperty(o: any, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): any;
171173

172174
/**
173175
* Adds one or more properties to an object, and/or modifies attributes of existing properties.
@@ -1340,7 +1342,7 @@ type Pick<T, K extends keyof T> = {
13401342
/**
13411343
* Construct a type with a set of properties K of type T
13421344
*/
1343-
type Record<K extends string, T> = {
1345+
type Record<K extends keyof any, T> = {
13441346
[P in K]: T;
13451347
};
13461348

tests/baselines/reference/api/tsserverlibrary.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2100,7 +2100,7 @@ declare namespace ts {
21002100
Unit = 13536,
21012101
StringOrNumberLiteral = 96,
21022102
PossiblyFalsy = 14574,
2103-
StringLike = 524322,
2103+
StringLike = 34,
21042104
NumberLike = 84,
21052105
BooleanLike = 136,
21062106
EnumLike = 272,
@@ -2340,6 +2340,7 @@ declare namespace ts {
23402340
inlineSources?: boolean;
23412341
isolatedModules?: boolean;
23422342
jsx?: JsxEmit;
2343+
keyofStringsOnly?: boolean;
23432344
lib?: string[];
23442345
locale?: string;
23452346
mapRoot?: string;

tests/baselines/reference/api/typescript.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2100,7 +2100,7 @@ declare namespace ts {
21002100
Unit = 13536,
21012101
StringOrNumberLiteral = 96,
21022102
PossiblyFalsy = 14574,
2103-
StringLike = 524322,
2103+
StringLike = 34,
21042104
NumberLike = 84,
21052105
BooleanLike = 136,
21062106
EnumLike = 272,
@@ -2340,6 +2340,7 @@ declare namespace ts {
23402340
inlineSources?: boolean;
23412341
isolatedModules?: boolean;
23422342
jsx?: JsxEmit;
2343+
keyofStringsOnly?: boolean;
23432344
lib?: string[];
23442345
locale?: string;
23452346
mapRoot?: string;

tests/baselines/reference/classAppearsToHaveMembersOfObject.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ var r = c.toString();
1717
var r2 = c.hasOwnProperty('');
1818
>r2 : boolean
1919
>c.hasOwnProperty('') : boolean
20-
>c.hasOwnProperty : (v: string) => boolean
20+
>c.hasOwnProperty : (v: string | number | symbol) => boolean
2121
>c : C
22-
>hasOwnProperty : (v: string) => boolean
22+
>hasOwnProperty : (v: string | number | symbol) => boolean
2323
>'' : ""
2424

2525
var o: Object = c;

tests/baselines/reference/complexRecursiveCollections.symbols

+4-4
Original file line numberDiff line numberDiff line change
@@ -1689,7 +1689,7 @@ declare module Immutable {
16891689
export interface Class<T extends Object> {
16901690
>Class : Symbol(Class, Decl(immutable.ts, 214, 70))
16911691
>T : Symbol(T, Decl(immutable.ts, 215, 27))
1692-
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
1692+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
16931693

16941694
(values?: Partial<T> | Iterable<[string, any]>): Instance<T> & Readonly<T>;
16951695
>values : Symbol(values, Decl(immutable.ts, 216, 7))
@@ -1714,7 +1714,7 @@ declare module Immutable {
17141714
export interface Instance<T extends Object> {
17151715
>Instance : Symbol(Instance, Decl(immutable.ts, 218, 5))
17161716
>T : Symbol(T, Decl(immutable.ts, 219, 30))
1717-
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
1717+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
17181718

17191719
readonly size: number;
17201720
>size : Symbol(Instance.size, Decl(immutable.ts, 219, 49))
@@ -2005,7 +2005,7 @@ declare module Immutable {
20052005

20062006
toJS(): Object;
20072007
>toJS : Symbol(Keyed.toJS, Decl(immutable.ts, 269, 76))
2008-
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
2008+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
20092009

20102010
toJSON(): { [key: string]: V };
20112011
>toJSON : Symbol(Keyed.toJSON, Decl(immutable.ts, 270, 21))
@@ -2594,7 +2594,7 @@ declare module Immutable {
25942594

25952595
toJS(): Object;
25962596
>toJS : Symbol(Keyed.toJS, Decl(immutable.ts, 340, 59))
2597-
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
2597+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
25982598

25992599
toJSON(): { [key: string]: V };
26002600
>toJSON : Symbol(Keyed.toJSON, Decl(immutable.ts, 341, 21))

tests/baselines/reference/computedPropertyNames10_ES5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ var a: any;
99
>a : any
1010

1111
var v = {
12-
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
13-
>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [<any>true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
12+
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
13+
>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [<any>true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
1414

1515
[s]() { },
1616
>[s] : () => void

tests/baselines/reference/computedPropertyNames10_ES6.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ var a: any;
99
>a : any
1010

1111
var v = {
12-
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
13-
>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [<any>true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
12+
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
13+
>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [<any>true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
1414

1515
[s]() { },
1616
>[s] : () => void

tests/baselines/reference/computedPropertyNames28_ES5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class C extends Base {
1212
>super : typeof Base
1313

1414
var obj = {
15-
>obj : { [x: string]: () => void; }
16-
>{ [(super(), "prop")]() { } } : { [x: string]: () => void; }
15+
>obj : { [(super(), "prop")](): void; }
16+
>{ [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }
1717

1818
[(super(), "prop")]() { }
1919
>[(super(), "prop")] : () => void

tests/baselines/reference/computedPropertyNames28_ES6.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class C extends Base {
1212
>super : typeof Base
1313

1414
var obj = {
15-
>obj : { [x: string]: () => void; }
16-
>{ [(super(), "prop")]() { } } : { [x: string]: () => void; }
15+
>obj : { [(super(), "prop")](): void; }
16+
>{ [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }
1717

1818
[(super(), "prop")]() { }
1919
>[(super(), "prop")] : () => void

tests/baselines/reference/computedPropertyNames30_ES5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class C extends Base {
1515
>() => { var obj = { // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } }; } : () => void
1616

1717
var obj = {
18-
>obj : { [x: string]: () => void; }
19-
>{ // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } } : { [x: string]: () => void; }
18+
>obj : { [(super(), "prop")](): void; }
19+
>{ // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }
2020

2121
// Ideally, we would capture this. But the reference is
2222
// illegal, and not capturing this is consistent with

tests/baselines/reference/computedPropertyNames30_ES6.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class C extends Base {
1515
>() => { var obj = { // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } }; } : () => void
1616

1717
var obj = {
18-
>obj : { [x: string]: () => void; }
19-
>{ // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } } : { [x: string]: () => void; }
18+
>obj : { [(super(), "prop")](): void; }
19+
>{ // Ideally, we would capture this. But the reference is // illegal, and not capturing this is consistent with //treatment of other similar violations. [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }
2020

2121
// Ideally, we would capture this. But the reference is
2222
// illegal, and not capturing this is consistent with

tests/baselines/reference/computedPropertyNames5_ES5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ var b: boolean;
33
>b : boolean
44

55
var v = {
6-
>v : { [x: string]: number; [x: number]: any; [true]: number; }
7-
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: string]: number; [x: number]: null; [true]: number; }
6+
>v : { [x: number]: any; }
7+
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: number]: null; }
88

99
[b]: 0,
1010
>[b] : number

tests/baselines/reference/computedPropertyNames5_ES6.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ var b: boolean;
33
>b : boolean
44

55
var v = {
6-
>v : { [x: string]: number; [x: number]: any; [true]: number; }
7-
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: string]: number; [x: number]: null; [true]: number; }
6+
>v : { [x: number]: any; }
7+
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: number]: null; }
88

99
[b]: 0,
1010
>[b] : number

tests/baselines/reference/computedPropertyNames9_ES5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ function f(x): any { }
1919
>x : any
2020

2121
var v = {
22-
>v : { [x: string]: number; [x: number]: number; [f(true)]: number; }
23-
>{ [f("")]: 0, [f(0)]: 0, [f(true)]: 0} : { [x: string]: number; [x: number]: number; [f(true)]: number; }
22+
>v : { [x: string]: number; [x: number]: number; }
23+
>{ [f("")]: 0, [f(0)]: 0, [f(true)]: 0} : { [x: string]: number; [x: number]: number; }
2424

2525
[f("")]: 0,
2626
>[f("")] : number

0 commit comments

Comments
 (0)