Skip to content

Commit

Permalink
Add ES2019 Object.fromEntries function (#30934)
Browse files Browse the repository at this point in the history
* add ES2019 Object.fromEntries function

* add some comments

* apply suggested changes

* add readonly and general any
  • Loading branch information
saschanaz authored and RyanCavanaugh committed Apr 30, 2019
1 parent 1d83982 commit 49d6f61
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/compiler/commandLineParser.ts
Expand Up @@ -45,6 +45,7 @@ namespace ts {
["es2018.promise", "lib.es2018.promise.d.ts"],
["es2018.regexp", "lib.es2018.regexp.d.ts"],
["es2019.array", "lib.es2019.array.d.ts"],
["es2019.object", "lib.es2019.object.d.ts"],
["es2019.string", "lib.es2019.string.d.ts"],
["es2019.symbol", "lib.es2019.symbol.d.ts"],
["es2020.string", "lib.es2020.string.d.ts"],
Expand Down
1 change: 1 addition & 0 deletions src/lib/es2019.d.ts
@@ -1,4 +1,5 @@
/// <reference lib="es2018" />
/// <reference lib="es2019.array" />
/// <reference lib="es2019.object" />
/// <reference lib="es2019.string" />
/// <reference lib="es2019.symbol" />
15 changes: 15 additions & 0 deletions src/lib/es2019.object.d.ts
@@ -0,0 +1,15 @@
/// <reference lib="es2015.iterable" />

interface ObjectConstructor {
/**
* Returns an object created by key-value entries for properties and methods
* @param entries An iterable object that contains key-value entries for properties and methods.
*/
fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k in PropertyKey]: T };

/**
* Returns an object created by key-value entries for properties and methods
* @param entries An iterable object that contains key-value entries for properties and methods.
*/
fromEntries(entries: Iterable<readonly any[]>): any;
}
1 change: 1 addition & 0 deletions src/lib/libs.json
Expand Up @@ -36,6 +36,7 @@
"es2018.promise",
"es2018.intl",
"es2019.array",
"es2019.object",
"es2019.string",
"es2019.symbol",
"es2020.string",
Expand Down
6 changes: 3 additions & 3 deletions src/testRunner/unittests/config/commandLineParsing.ts
Expand Up @@ -57,7 +57,7 @@ namespace ts {
assertParseResult(["--lib", "es5,invalidOption", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand Down Expand Up @@ -259,7 +259,7 @@ namespace ts {
assertParseResult(["--lib", "es5,", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand All @@ -278,7 +278,7 @@ namespace ts {
assertParseResult(["--lib", "es5, ", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand Down
20 changes: 20 additions & 0 deletions tests/baselines/reference/objectFromEntries.js
@@ -0,0 +1,20 @@
//// [objectFromEntries.ts]
const o = Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]);
const o2 = Object.fromEntries(new URLSearchParams());
const o3 = Object.fromEntries(new Map([[Symbol("key"), "value"]]));

const frozenArray = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o4 = Object.fromEntries(frozenArray);

const frozenArray2: readonly [string, number][] = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o5 = Object.fromEntries(frozenArray2);


//// [objectFromEntries.js]
const o = Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]);
const o2 = Object.fromEntries(new URLSearchParams());
const o3 = Object.fromEntries(new Map([[Symbol("key"), "value"]]));
const frozenArray = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o4 = Object.fromEntries(frozenArray);
const frozenArray2 = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o5 = Object.fromEntries(frozenArray2);
48 changes: 48 additions & 0 deletions tests/baselines/reference/objectFromEntries.symbols
@@ -0,0 +1,48 @@
=== tests/cases/compiler/objectFromEntries.ts ===
const o = Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]);
>o : Symbol(o, Decl(objectFromEntries.ts, 0, 5))
>Object.fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))

const o2 = Object.fromEntries(new URLSearchParams());
>o2 : Symbol(o2, Decl(objectFromEntries.ts, 1, 5))
>Object.fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>URLSearchParams : Symbol(URLSearchParams, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(lib.dom.iterable.d.ts, --, --))

const o3 = Object.fromEntries(new Map([[Symbol("key"), "value"]]));
>o3 : Symbol(o3, Decl(objectFromEntries.ts, 2, 5))
>Object.fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))

const frozenArray = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
>frozenArray : Symbol(frozenArray, Decl(objectFromEntries.ts, 4, 5))
>Object.freeze : Symbol(ObjectConstructor.freeze, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>freeze : Symbol(ObjectConstructor.freeze, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

const o4 = Object.fromEntries(frozenArray);
>o4 : Symbol(o4, Decl(objectFromEntries.ts, 5, 5))
>Object.fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>frozenArray : Symbol(frozenArray, Decl(objectFromEntries.ts, 4, 5))

const frozenArray2: readonly [string, number][] = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
>frozenArray2 : Symbol(frozenArray2, Decl(objectFromEntries.ts, 7, 5))
>Object.freeze : Symbol(ObjectConstructor.freeze, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>freeze : Symbol(ObjectConstructor.freeze, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

const o5 = Object.fromEntries(frozenArray2);
>o5 : Symbol(o5, Decl(objectFromEntries.ts, 8, 5))
>Object.fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>fromEntries : Symbol(ObjectConstructor.fromEntries, Decl(lib.es2019.object.d.ts, --, --), Decl(lib.es2019.object.d.ts, --, --))
>frozenArray2 : Symbol(frozenArray2, Decl(objectFromEntries.ts, 7, 5))

92 changes: 92 additions & 0 deletions tests/baselines/reference/objectFromEntries.types
@@ -0,0 +1,92 @@
=== tests/cases/compiler/objectFromEntries.ts ===
const o = Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]);
>o : { [x: string]: number; [x: number]: number; }
>Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]) : { [x: string]: number; [x: number]: number; }
>Object.fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>Object : ObjectConstructor
>fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>[['a', 1], ['b', 2], ['c', 3]] : [string, number][]
>['a', 1] : [string, number]
>'a' : "a"
>1 : 1
>['b', 2] : [string, number]
>'b' : "b"
>2 : 2
>['c', 3] : [string, number]
>'c' : "c"
>3 : 3

const o2 = Object.fromEntries(new URLSearchParams());
>o2 : { [x: string]: string; [x: number]: string; }
>Object.fromEntries(new URLSearchParams()) : { [x: string]: string; [x: number]: string; }
>Object.fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>Object : ObjectConstructor
>fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>new URLSearchParams() : URLSearchParams
>URLSearchParams : { new (init?: string | URLSearchParams | string[][] | Record<string, string>): URLSearchParams; prototype: URLSearchParams; }

const o3 = Object.fromEntries(new Map([[Symbol("key"), "value"]]));
>o3 : { [x: string]: string; [x: number]: string; }
>Object.fromEntries(new Map([[Symbol("key"), "value"]])) : { [x: string]: string; [x: number]: string; }
>Object.fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>Object : ObjectConstructor
>fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>new Map([[Symbol("key"), "value"]]) : Map<symbol, string>
>Map : MapConstructor
>[[Symbol("key"), "value"]] : [symbol, string][]
>[Symbol("key"), "value"] : [symbol, string]
>Symbol("key") : symbol
>Symbol : SymbolConstructor
>"key" : "key"
>"value" : "value"

const frozenArray = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
>frozenArray : readonly (string | number)[][]
>Object.freeze([['a', 1], ['b', 2], ['c', 3]]) : readonly (string | number)[][]
>Object.freeze : { <T>(a: T[]): readonly T[]; <T extends Function>(f: T): T; <T>(o: T): Readonly<T>; }
>Object : ObjectConstructor
>freeze : { <T>(a: T[]): readonly T[]; <T extends Function>(f: T): T; <T>(o: T): Readonly<T>; }
>[['a', 1], ['b', 2], ['c', 3]] : (string | number)[][]
>['a', 1] : (string | number)[]
>'a' : "a"
>1 : 1
>['b', 2] : (string | number)[]
>'b' : "b"
>2 : 2
>['c', 3] : (string | number)[]
>'c' : "c"
>3 : 3

const o4 = Object.fromEntries(frozenArray);
>o4 : any
>Object.fromEntries(frozenArray) : any
>Object.fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>Object : ObjectConstructor
>fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>frozenArray : readonly (string | number)[][]

const frozenArray2: readonly [string, number][] = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
>frozenArray2 : readonly [string, number][]
>Object.freeze([['a', 1], ['b', 2], ['c', 3]]) : readonly [string, number][]
>Object.freeze : { <T>(a: T[]): readonly T[]; <T extends Function>(f: T): T; <T>(o: T): Readonly<T>; }
>Object : ObjectConstructor
>freeze : { <T>(a: T[]): readonly T[]; <T extends Function>(f: T): T; <T>(o: T): Readonly<T>; }
>[['a', 1], ['b', 2], ['c', 3]] : [string, number][]
>['a', 1] : [string, number]
>'a' : "a"
>1 : 1
>['b', 2] : [string, number]
>'b' : "b"
>2 : 2
>['c', 3] : [string, number]
>'c' : "c"
>3 : 3

const o5 = Object.fromEntries(frozenArray2);
>o5 : { [x: string]: number; [x: number]: number; }
>Object.fromEntries(frozenArray2) : { [x: string]: number; [x: number]: number; }
>Object.fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>Object : ObjectConstructor
>fromEntries : { <T = any>(entries: Iterable<readonly [string | number | symbol, T]>): { [x: string]: T; [x: number]: T; }; (entries: Iterable<readonly any[]>): any; }
>frozenArray2 : readonly [string, number][]

11 changes: 11 additions & 0 deletions tests/cases/compiler/objectFromEntries.ts
@@ -0,0 +1,11 @@
// @target: es2019

const o = Object.fromEntries([['a', 1], ['b', 2], ['c', 3]]);
const o2 = Object.fromEntries(new URLSearchParams());
const o3 = Object.fromEntries(new Map([[Symbol("key"), "value"]]));

const frozenArray = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o4 = Object.fromEntries(frozenArray);

const frozenArray2: readonly [string, number][] = Object.freeze([['a', 1], ['b', 2], ['c', 3]]);
const o5 = Object.fromEntries(frozenArray2);

0 comments on commit 49d6f61

Please sign in to comment.