Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support number and symbol named properties with keyof and mapped types #23592

Merged
merged 27 commits into from
Apr 23, 2018
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a8d6a45
Move nameType from SymbolLinks to TransientSymbol
ahejlsberg Apr 5, 2018
39bb93f
Move nameType to base Symbol, generate nameType properties where missing
ahejlsberg Apr 5, 2018
fc85ba9
Limit getLiteralTypeFromPropertyName to only return string-like types
ahejlsberg Apr 6, 2018
ccf20d3
Accept new baselines
ahejlsberg Apr 6, 2018
ff20f38
Add support for numbers and symbols in keyof (but keep it disabled)
ahejlsberg Apr 9, 2018
68ce69a
Move 'PropertyKey' from es2015.core.d.ts to es5.d.ts
ahejlsberg Apr 9, 2018
2c02195
Accept new baselines
ahejlsberg Apr 9, 2018
6d93f30
Enable 'keyof T' for full string | number | symbol
ahejlsberg Apr 9, 2018
0379666
Update tests
ahejlsberg Apr 9, 2018
b1545fe
Accept new baselines
ahejlsberg Apr 9, 2018
b40592c
String index signatures do not apply to symbols
ahejlsberg Apr 11, 2018
0a37cd3
Update test
ahejlsberg Apr 11, 2018
b746f8e
Accept new baselines
ahejlsberg Apr 11, 2018
c344e6d
Fixes and improvements to indexed access type relationships
ahejlsberg Apr 15, 2018
6c60f7e
Accept new baselines
ahejlsberg Apr 15, 2018
8cb515a
Add members for numeric and symbol named properties in mapped types
ahejlsberg Apr 16, 2018
16cd558
Merge branch 'master' into improveIndexTypes
ahejlsberg Apr 16, 2018
b11be80
Don't widen unique symbol types during type inference
ahejlsberg Apr 17, 2018
02534cc
Accept new baselines
ahejlsberg Apr 17, 2018
b14d389
For 'T extends { [x: string]: XXX }' constraint of T[keyof T] is XXX
ahejlsberg Apr 18, 2018
254782c
Accept new baselines
ahejlsberg Apr 18, 2018
9e4e215
Revise IndexType to have stringsOnly property
ahejlsberg Apr 18, 2018
5f0d880
Update test
ahejlsberg Apr 18, 2018
eb7bbfb
Properties with numeric names have numeric literal types in keyof T
ahejlsberg Apr 20, 2018
b38e42e
Accept new baselines
ahejlsberg Apr 20, 2018
652e493
Address CR feedback
ahejlsberg Apr 21, 2018
c7f55be
Accept new baselines
ahejlsberg Apr 21, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 227 additions & 195 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,12 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types,
},
{
name: "keyofStringsOnly",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Resolve_keyof_to_string_valued_property_names_only_no_numbers_or_symbols,
},
{
// A list of plugins to load in the language service
name: "plugins",
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3530,6 +3530,10 @@
"category": "Message",
"code": 6194
},
"Resolve 'keyof' to string valued property names only (no numbers or symbols).": {
"category": "Message",
"code": 6195
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
21 changes: 11 additions & 10 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3369,6 +3369,7 @@ namespace ts {
members?: SymbolTable; // Class, interface or object literal instance members
exports?: SymbolTable; // Module exports
globalExports?: SymbolTable; // Conditional global UMD exports
nameType?: Type; // Type associated with a late-bound symbol
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
/* @internal */ parent?: Symbol; // Parent symbol
Expand Down Expand Up @@ -3407,7 +3408,6 @@ namespace ts {
enumKind?: EnumKind; // Enum declaration classification
originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol`
lateSymbol?: Symbol; // Late-bound symbol for a computed property
nameType?: Type; // Type associate with a late-bound or mapped type property symbol's name
}

/* @internal */
Expand Down Expand Up @@ -3604,7 +3604,7 @@ namespace ts {
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
/* @internal */
Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol,
StringLike = String | StringLiteral | Index,
StringLike = String | StringLiteral,
NumberLike = Number | NumberLiteral | Enum,
BooleanLike = Boolean | BooleanLiteral,
EnumLike = Enum | EnumLiteral,
Expand Down Expand Up @@ -3761,7 +3761,7 @@ namespace ts {
/* @internal */
resolvedIndexType: IndexType;
/* @internal */
resolvedDeclaredIndexType: IndexType;
resolvedStringIndexType: IndexType;
/* @internal */
resolvedBaseConstraint: Type;
/* @internal */
Expand Down Expand Up @@ -3850,7 +3850,7 @@ namespace ts {
/* @internal */
resolvedIndexType?: IndexType;
/* @internal */
resolvedDeclaredIndexType?: IndexType;
resolvedStringIndexType?: IndexType;
}

// Type parameters (TypeFlags.TypeParameter)
Expand Down Expand Up @@ -3882,9 +3882,9 @@ namespace ts {

// keyof T types (TypeFlags.Index)
export interface IndexType extends InstantiableType {
/* @internal */
isDeclaredType?: boolean;
type: InstantiableType | UnionOrIntersectionType;
/* @internal */
stringsOnly: boolean;
}

export interface ConditionalRoot {
Expand Down Expand Up @@ -4043,10 +4043,10 @@ namespace ts {

/* @internal */
export interface WideningContext {
parent?: WideningContext; // Parent context
propertyName?: __String; // Name of property in parent
siblings?: Type[]; // Types of siblings
resolvedPropertyNames?: __String[]; // Property names occurring in sibling object literals
parent?: WideningContext; // Parent context
propertyName?: __String; // Name of property in parent
siblings?: Type[]; // Types of siblings
resolvedProperties?: Symbol[]; // Properties occurring in sibling object literals
}

/* @internal */
Expand Down Expand Up @@ -4161,6 +4161,7 @@ namespace ts {
inlineSources?: boolean;
isolatedModules?: boolean;
jsx?: JsxEmit;
keyofStringsOnly?: boolean;
lib?: string[];
/*@internal*/listEmittedFiles?: boolean;
/*@internal*/listFiles?: boolean;
Expand Down
35 changes: 0 additions & 35 deletions src/lib/es2015.core.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
declare type PropertyKey = string | number | symbol;

interface Array<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
Expand Down Expand Up @@ -258,20 +256,6 @@ interface NumberConstructor {
parseInt(string: string, radix?: number): number;
}

interface Object {
/**
* Determines whether an object has a property with the specified name.
* @param v A property name.
*/
hasOwnProperty(v: PropertyKey): boolean;

/**
* Determines whether a specified property is enumerable.
* @param v A property name.
*/
propertyIsEnumerable(v: PropertyKey): boolean;
}

interface ObjectConstructor {
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
Expand Down Expand Up @@ -327,25 +311,6 @@ interface ObjectConstructor {
* @param proto The value of the new prototype or null.
*/
setPrototypeOf(o: any, proto: object | null): any;

/**
* Gets the own property descriptor of the specified object.
* An own property descriptor is one that is defined directly on the object and is not
* inherited from the object's prototype.
* @param o Object that contains the property.
* @param p Name of the property.
*/
getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor | undefined;

/**
* Adds a property to an object, or modifies attributes of an existing property.
* @param o Object on which to add or modify the property. This can be a native JavaScript
* object (that is, a user-defined object or a built in object) or a DOM object.
* @param p The property name.
* @param attributes Descriptor for the property. It can be for a data property or an accessor
* property.
*/
defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any;
}

interface ReadonlyArray<T> {
Expand Down
12 changes: 7 additions & 5 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ declare function escape(string: string): string;
*/
declare function unescape(string: string): string;

declare type PropertyKey = string | number | symbol;

interface PropertyDescriptor {
configurable?: boolean;
enumerable?: boolean;
Expand Down Expand Up @@ -104,7 +106,7 @@ interface Object {
* Determines whether an object has a property with the specified name.
* @param v A property name.
*/
hasOwnProperty(v: string): boolean;
hasOwnProperty(v: PropertyKey): boolean;

/**
* Determines whether an object exists in another object's prototype chain.
Expand All @@ -116,7 +118,7 @@ interface Object {
* Determines whether a specified property is enumerable.
* @param v A property name.
*/
propertyIsEnumerable(v: string): boolean;
propertyIsEnumerable(v: PropertyKey): boolean;
}

interface ObjectConstructor {
Expand All @@ -139,7 +141,7 @@ interface ObjectConstructor {
* @param o Object that contains the property.
* @param p Name of the property.
*/
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor | undefined;
getOwnPropertyDescriptor(o: any, p: PropertyKey): PropertyDescriptor | undefined;

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

/**
* Adds one or more properties to an object, and/or modifies attributes of existing properties.
Expand Down Expand Up @@ -1340,7 +1342,7 @@ type Pick<T, K extends keyof T> = {
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends string, T> = {
type Record<K extends keyof any, T> = {
[P in K]: T;
};

Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2020,6 +2020,7 @@ declare namespace ts {
members?: SymbolTable;
exports?: SymbolTable;
globalExports?: SymbolTable;
nameType?: Type;
}
enum InternalSymbolName {
Call = "__call",
Expand Down Expand Up @@ -2099,7 +2100,7 @@ declare namespace ts {
Unit = 13536,
StringOrNumberLiteral = 96,
PossiblyFalsy = 14574,
StringLike = 524322,
StringLike = 34,
NumberLike = 84,
BooleanLike = 136,
EnumLike = 272,
Expand Down Expand Up @@ -2339,6 +2340,7 @@ declare namespace ts {
inlineSources?: boolean;
isolatedModules?: boolean;
jsx?: JsxEmit;
keyofStringsOnly?: boolean;
lib?: string[];
locale?: string;
mapRoot?: string;
Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2020,6 +2020,7 @@ declare namespace ts {
members?: SymbolTable;
exports?: SymbolTable;
globalExports?: SymbolTable;
nameType?: Type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be public (given that it's kinda lazy, probably not)? Previously it was on TransientSymbol, which was @internal'd.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can make it internal.

}
enum InternalSymbolName {
Call = "__call",
Expand Down Expand Up @@ -2099,7 +2100,7 @@ declare namespace ts {
Unit = 13536,
StringOrNumberLiteral = 96,
PossiblyFalsy = 14574,
StringLike = 524322,
StringLike = 34,
NumberLike = 84,
BooleanLike = 136,
EnumLike = 272,
Expand Down Expand Up @@ -2339,6 +2340,7 @@ declare namespace ts {
inlineSources?: boolean;
isolatedModules?: boolean;
jsx?: JsxEmit;
keyofStringsOnly?: boolean;
lib?: string[];
locale?: string;
mapRoot?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ var r = c.toString();
var r2 = c.hasOwnProperty('');
>r2 : boolean
>c.hasOwnProperty('') : boolean
>c.hasOwnProperty : (v: string) => boolean
>c.hasOwnProperty : (v: string | number | symbol) => boolean
>c : C
>hasOwnProperty : (v: string) => boolean
>hasOwnProperty : (v: string | number | symbol) => boolean
>'' : ""

var o: Object = c;
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/complexRecursiveCollections.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -1689,7 +1689,7 @@ declare module Immutable {
export interface Class<T extends Object> {
>Class : Symbol(Class, Decl(immutable.ts, 214, 70))
>T : Symbol(T, Decl(immutable.ts, 215, 27))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

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

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

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

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

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

toJSON(): { [key: string]: V };
>toJSON : Symbol(Keyed.toJSON, Decl(immutable.ts, 341, 21))
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames10_ES5.types
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ var a: any;
>a : any

var v = {
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
>{ [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; }
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
>{ [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; }

[s]() { },
>[s] : () => void
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames10_ES6.types
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ var a: any;
>a : any

var v = {
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; }
>{ [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; }
>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; [`hello bye`](): void; }
>{ [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; }

[s]() { },
>[s] : () => void
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames28_ES5.types
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class C extends Base {
>super : typeof Base

var obj = {
>obj : { [x: string]: () => void; }
>{ [(super(), "prop")]() { } } : { [x: string]: () => void; }
>obj : { [(super(), "prop")](): void; }
>{ [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }

[(super(), "prop")]() { }
>[(super(), "prop")] : () => void
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames28_ES6.types
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class C extends Base {
>super : typeof Base

var obj = {
>obj : { [x: string]: () => void; }
>{ [(super(), "prop")]() { } } : { [x: string]: () => void; }
>obj : { [(super(), "prop")](): void; }
>{ [(super(), "prop")]() { } } : { [(super(), "prop")](): void; }

[(super(), "prop")]() { }
>[(super(), "prop")] : () => void
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames30_ES5.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class C extends Base {
>() => { 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

var obj = {
>obj : { [x: string]: () => void; }
>{ // 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; }
>obj : { [(super(), "prop")](): void; }
>{ // 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; }

// Ideally, we would capture this. But the reference is
// illegal, and not capturing this is consistent with
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames30_ES6.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class C extends Base {
>() => { 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

var obj = {
>obj : { [x: string]: () => void; }
>{ // 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; }
>obj : { [(super(), "prop")](): void; }
>{ // 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; }

// Ideally, we would capture this. But the reference is
// illegal, and not capturing this is consistent with
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames5_ES5.types
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ var b: boolean;
>b : boolean

var v = {
>v : { [x: string]: number; [x: number]: any; [true]: number; }
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: string]: number; [x: number]: null; [true]: number; }
>v : { [x: number]: any; }
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: number]: null; }

[b]: 0,
>[b] : number
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/computedPropertyNames5_ES6.types
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ var b: boolean;
>b : boolean

var v = {
>v : { [x: string]: number; [x: number]: any; [true]: number; }
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: string]: number; [x: number]: null; [true]: number; }
>v : { [x: number]: any; }
>{ [b]: 0, [true]: 1, [[]]: 0, [{}]: 0, [undefined]: undefined, [null]: null} : { [x: number]: null; }

[b]: 0,
>[b] : number
Expand Down
Loading