From 5599d05252c3453714fa9d093f6db794cca93bb9 Mon Sep 17 00:00:00 2001 From: Ethan Resnick Date: Mon, 14 Nov 2016 20:51:28 -0500 Subject: [PATCH] Object.entries() types: fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Special case array. Because array instances are typed as implementing an interface that lists all their public methods, `keyof Array` returns all those method names as keys…even though they’re not actually enumerable at runtime. 2. Intersect keyof T with string, because `keyof T` can return `number | string` in some cases, whereas the entries’ keys will always be strings. --- src/lib/es2017.object.d.ts | 3 +- .../reference/useObjectValuesAndEntries1.js | 21 +++++-- .../useObjectValuesAndEntries1.symbols | 40 ++++++++---- .../useObjectValuesAndEntries1.types | 61 +++++++++++++------ .../useObjectValuesAndEntries4.symbols | 4 +- .../useObjectValuesAndEntries4.types | 8 +-- .../es2017/useObjectValuesAndEntries1.ts | 11 +++- 7 files changed, 104 insertions(+), 44 deletions(-) diff --git a/src/lib/es2017.object.d.ts b/src/lib/es2017.object.d.ts index c219f467ac985..1bae8513b4ed2 100644 --- a/src/lib/es2017.object.d.ts +++ b/src/lib/es2017.object.d.ts @@ -9,6 +9,7 @@ interface ObjectConstructor { * Returns an array of key/values of the enumerable properties of an object * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. */ - entries(o: T): [keyof T, T[K]][]; + entries(o: Array): [string, T][]; + entries(o: T): [keyof T & string, T[K]][]; entries(o: any): [string, any][]; } diff --git a/tests/baselines/reference/useObjectValuesAndEntries1.js b/tests/baselines/reference/useObjectValuesAndEntries1.js index 728e14e746363..d6d538e239464 100644 --- a/tests/baselines/reference/useObjectValuesAndEntries1.js +++ b/tests/baselines/reference/useObjectValuesAndEntries1.js @@ -6,10 +6,15 @@ for (var x of Object.values(o)) { let y = x; } -var entries = Object.entries(o); // <-- entries: ['a' | 'b', number][] +var entries = Object.entries(o); // <-- entries: [('a' & string) | ('b' & string), number][] var entries1 = Object.entries(1); // <-- entries: [string, any][] -var entries2 = Object.entries({a: true, b: 2}) // ['a' | 'b', number | boolean][] -var entries3 = Object.entries({}) // [never, any][] +var entries2 = Object.entries({a: true, b: 2}) // [('a' & string) | ('b' & string), number | boolean][] +var entries3 = Object.entries({}) // [string, any][] +var entries4 = Object.entries([1, 2, 3, 4]); // [string, number][] + +// type below should be [string | (string & number), any] NOT [string | number, any] +var x2: { [index: string]: any } = {1: 2}; +var entries5 = Object.entries(x2); //// [useObjectValuesAndEntries1.js] @@ -18,7 +23,11 @@ for (var _i = 0, _a = Object.values(o); _i < _a.length; _i++) { var x = _a[_i]; var y = x; } -var entries = Object.entries(o); // <-- entries: ['a' | 'b', number][] +var entries = Object.entries(o); // <-- entries: [('a' & string) | ('b' & string), number][] var entries1 = Object.entries(1); // <-- entries: [string, any][] -var entries2 = Object.entries({ a: true, b: 2 }); // ['a' | 'b', number | boolean][] -var entries3 = Object.entries({}); // [never, any][] +var entries2 = Object.entries({ a: true, b: 2 }); // [('a' & string) | ('b' & string), number | boolean][] +var entries3 = Object.entries({}); // [string, any][] +var entries4 = Object.entries([1, 2, 3, 4]); // [string, number][] +// type below should be [string | (string & number), any] NOT [string | number, any] +var x2 = { 1: 2 }; +var entries5 = Object.entries(x2); diff --git a/tests/baselines/reference/useObjectValuesAndEntries1.symbols b/tests/baselines/reference/useObjectValuesAndEntries1.symbols index 521d3ae722eb3..f915c34608a18 100644 --- a/tests/baselines/reference/useObjectValuesAndEntries1.symbols +++ b/tests/baselines/reference/useObjectValuesAndEntries1.symbols @@ -17,30 +17,48 @@ for (var x of Object.values(o)) { >x : Symbol(x, Decl(useObjectValuesAndEntries1.ts, 3, 8)) } -var entries = Object.entries(o); // <-- entries: ['a' | 'b', number][] +var entries = Object.entries(o); // <-- entries: [('a' & string) | ('b' & string), number][] >entries : Symbol(entries, Decl(useObjectValuesAndEntries1.ts, 7, 3)) ->Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >o : Symbol(o, Decl(useObjectValuesAndEntries1.ts, 1, 3)) var entries1 = Object.entries(1); // <-- entries: [string, any][] >entries1 : Symbol(entries1, Decl(useObjectValuesAndEntries1.ts, 8, 3)) ->Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) -var entries2 = Object.entries({a: true, b: 2}) // ['a' | 'b', number | boolean][] +var entries2 = Object.entries({a: true, b: 2}) // [('a' & string) | ('b' & string), number | boolean][] >entries2 : Symbol(entries2, Decl(useObjectValuesAndEntries1.ts, 9, 3)) ->Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >a : Symbol(a, Decl(useObjectValuesAndEntries1.ts, 9, 31)) >b : Symbol(b, Decl(useObjectValuesAndEntries1.ts, 9, 39)) -var entries3 = Object.entries({}) // [never, any][] +var entries3 = Object.entries({}) // [string, any][] >entries3 : Symbol(entries3, Decl(useObjectValuesAndEntries1.ts, 10, 3)) ->Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) + +var entries4 = Object.entries([1, 2, 3, 4]); // [string, number][] +>entries4 : Symbol(entries4, Decl(useObjectValuesAndEntries1.ts, 11, 3)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) + +// type below should be [string | (string & number), any] NOT [string | number, any] +var x2: { [index: string]: any } = {1: 2}; +>x2 : Symbol(x2, Decl(useObjectValuesAndEntries1.ts, 14, 3)) +>index : Symbol(index, Decl(useObjectValuesAndEntries1.ts, 14, 11)) + +var entries5 = Object.entries(x2); +>entries5 : Symbol(entries5, Decl(useObjectValuesAndEntries1.ts, 15, 3)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>x2 : Symbol(x2, Decl(useObjectValuesAndEntries1.ts, 14, 3)) diff --git a/tests/baselines/reference/useObjectValuesAndEntries1.types b/tests/baselines/reference/useObjectValuesAndEntries1.types index f04201450c00f..b326d56ab44a7 100644 --- a/tests/baselines/reference/useObjectValuesAndEntries1.types +++ b/tests/baselines/reference/useObjectValuesAndEntries1.types @@ -21,39 +21,66 @@ for (var x of Object.values(o)) { >x : number } -var entries = Object.entries(o); // <-- entries: ['a' | 'b', number][] ->entries : ["a" | "b", number][] ->Object.entries(o) : ["a" | "b", number][] ->Object.entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +var entries = Object.entries(o); // <-- entries: [('a' & string) | ('b' & string), number][] +>entries : [("a" & string) | ("b" & string), number][] +>Object.entries(o) : [("a" & string) | ("b" & string), number][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >Object : ObjectConstructor ->entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >o : { a: number; b: number; } var entries1 = Object.entries(1); // <-- entries: [string, any][] >entries1 : [string, any][] >Object.entries(1) : [string, any][] ->Object.entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >Object : ObjectConstructor ->entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >1 : 1 -var entries2 = Object.entries({a: true, b: 2}) // ['a' | 'b', number | boolean][] ->entries2 : ["a" | "b", number | boolean][] ->Object.entries({a: true, b: 2}) : ["a" | "b", number | boolean][] ->Object.entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +var entries2 = Object.entries({a: true, b: 2}) // [('a' & string) | ('b' & string), number | boolean][] +>entries2 : [("a" & string) | ("b" & string), number | boolean][] +>Object.entries({a: true, b: 2}) : [("a" & string) | ("b" & string), number | boolean][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >Object : ObjectConstructor ->entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >{a: true, b: 2} : { a: true; b: number; } >a : boolean >true : true >b : number >2 : 2 -var entries3 = Object.entries({}) // [never, any][] ->entries3 : [never, any][] ->Object.entries({}) : [never, any][] ->Object.entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +var entries3 = Object.entries({}) // [string, any][] +>entries3 : [string, any][] +>Object.entries({}) : [string, any][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >Object : ObjectConstructor ->entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >{} : {} +var entries4 = Object.entries([1, 2, 3, 4]); // [string, number][] +>entries4 : [string, number][] +>Object.entries([1, 2, 3, 4]) : [string, number][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } +>Object : ObjectConstructor +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } +>[1, 2, 3, 4] : number[] +>1 : 1 +>2 : 2 +>3 : 3 +>4 : 4 + +// type below should be [string | (string & number), any] NOT [string | number, any] +var x2: { [index: string]: any } = {1: 2}; +>x2 : { [index: string]: any; } +>index : string +>{1: 2} : { 1: number; } +>2 : 2 + +var entries5 = Object.entries(x2); +>entries5 : [string | (number & string), any][] +>Object.entries(x2) : [string | (number & string), any][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } +>Object : ObjectConstructor +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } +>x2 : { [index: string]: any; } + diff --git a/tests/baselines/reference/useObjectValuesAndEntries4.symbols b/tests/baselines/reference/useObjectValuesAndEntries4.symbols index 5edf23790cd92..479dc842bc301 100644 --- a/tests/baselines/reference/useObjectValuesAndEntries4.symbols +++ b/tests/baselines/reference/useObjectValuesAndEntries4.symbols @@ -19,8 +19,8 @@ for (var x of Object.values(o)) { var entries = Object.entries(o); >entries : Symbol(entries, Decl(useObjectValuesAndEntries4.ts, 7, 3)) ->Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) ->entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) +>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --)) >o : Symbol(o, Decl(useObjectValuesAndEntries4.ts, 1, 3)) diff --git a/tests/baselines/reference/useObjectValuesAndEntries4.types b/tests/baselines/reference/useObjectValuesAndEntries4.types index d68193993e2b6..a1f70681033b3 100644 --- a/tests/baselines/reference/useObjectValuesAndEntries4.types +++ b/tests/baselines/reference/useObjectValuesAndEntries4.types @@ -22,10 +22,10 @@ for (var x of Object.values(o)) { } var entries = Object.entries(o); ->entries : ["a" | "b", number][] ->Object.entries(o) : ["a" | "b", number][] ->Object.entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : [("a" & string) | ("b" & string), number][] +>Object.entries(o) : [("a" & string) | ("b" & string), number][] +>Object.entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >Object : ObjectConstructor ->entries : { (o: T): [keyof T, T[K]][]; (o: any): [string, any][]; } +>entries : { (o: T[]): [string, T][]; (o: T): [keyof T & string, T[K]][]; (o: any): [string, any][]; } >o : { a: number; b: number; } diff --git a/tests/cases/conformance/es2017/useObjectValuesAndEntries1.ts b/tests/cases/conformance/es2017/useObjectValuesAndEntries1.ts index 60099bb1c0f6d..683747b02c54c 100644 --- a/tests/cases/conformance/es2017/useObjectValuesAndEntries1.ts +++ b/tests/cases/conformance/es2017/useObjectValuesAndEntries1.ts @@ -7,7 +7,12 @@ for (var x of Object.values(o)) { let y = x; } -var entries = Object.entries(o); // <-- entries: ['a' | 'b', number][] +var entries = Object.entries(o); // <-- entries: [('a' & string) | ('b' & string), number][] var entries1 = Object.entries(1); // <-- entries: [string, any][] -var entries2 = Object.entries({a: true, b: 2}) // ['a' | 'b', number | boolean][] -var entries3 = Object.entries({}) // [never, any][] +var entries2 = Object.entries({a: true, b: 2}) // [('a' & string) | ('b' & string), number | boolean][] +var entries3 = Object.entries({}) // [string, any][] +var entries4 = Object.entries([1, 2, 3, 4]); // [string, number][] + +// type below should be [string | (string & number), any] NOT [string | number, any] +var x2: { [index: string]: any } = {1: 2}; +var entries5 = Object.entries(x2);