From f0358f1e5dc28744828a062ba8b8294528d82456 Mon Sep 17 00:00:00 2001 From: Derek Wickern Date: Mon, 18 Sep 2017 18:07:46 -0700 Subject: [PATCH 1/5] add unsafe path methods proof-of-concept --- types/ember/index.d.ts | 18 +++++++-------- types/ember/test/unsafe.ts | 47 ++++++++++++++++++++++++++++++++++++++ types/ember/tsconfig.json | 3 ++- 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 types/ember/test/unsafe.ts diff --git a/types/ember/index.d.ts b/types/ember/index.d.ts index 7bcdeb2..6d03b55 100644 --- a/types/ember/index.d.ts +++ b/types/ember/index.d.ts @@ -809,14 +809,14 @@ export namespace Ember { /** * Alias for `mapBy` */ - getEach(key: string): any[]; + getEach(key: K): T[K][]; /** * Sets the value on the named property for each member. This is more * ergonomic than using other methods defined on this helper. If the object * implements Ember.Observable, the value will be changed to `set(),` otherwise * it will be set directly. `null` objects are skipped. */ - setEach(key: string, value: any): any; + setEach(key: K, value: T[K]): T[K]; /** * Maps all of the items in the enumeration to another value, returning * a new array. This method corresponds to `map()` defined in JavaScript 1.6. @@ -826,7 +826,7 @@ export namespace Ember { * Similar to map, this specialized function returns the value of the named * property on all items in the enumeration. */ - mapBy(key: string): any[]; + mapBy(key: K): T[K][]; /** * Returns an array with all of the items in the enumeration that the passed * function returns true for. This method corresponds to `filter()` defined in @@ -844,13 +844,13 @@ export namespace Ember { * can pass an optional second argument with the target value. Otherwise * this will match any property that evaluates to `true`. */ - filterBy(key: string, value?: any): any[]; + filterBy(key: K, value?: T[K]): T[]; /** * Returns an array with the items that do not have truthy values for * key. You can pass an optional second argument with the target value. Otherwise * this will match any property that evaluates to false. */ - rejectBy(key: string, value?: string): any[]; + rejectBy(key: K, value?: T[K]): T[]; /** * Returns the first item in the array for which the callback returns true. * This method works similar to the `filter()` method defined in JavaScript 1.6 @@ -862,7 +862,7 @@ export namespace Ember { * can pass an optional second argument with the target value. Otherwise * this will match any property that evaluates to `true`. */ - findBy(key: string, value: string): T | undefined; + findBy(key: K, value: T[K]): T | undefined; /** * Returns `true` if the passed function returns true for every item in the * enumeration. This corresponds with the `every()` method in JavaScript 1.6. @@ -873,7 +873,7 @@ export namespace Ember { * argument for all items in the enumerable. This method is often simpler/faster * than using a callback. */ - isEvery(key: string, value: boolean): boolean; + isEvery(key: keyof T, value?: boolean): boolean; /** * Returns `true` if the passed function returns true for any item in the * enumeration. @@ -884,7 +884,7 @@ export namespace Ember { * argument for any item in the enumerable. This method is often simpler/faster * than using a callback. */ - isAny(key: string, value?: boolean): boolean; + isAny(key: keyof T, value?: boolean): boolean; /** * This will combine the values of the enumerator into a single value. It * is a useful way to collect a summary value from an enumeration. This @@ -922,7 +922,7 @@ export namespace Ember { * Converts the enumerable into an array and sorts by the keys * specified in the argument. */ - sortBy(property: string): Enumerable; + sortBy(...property: (keyof T)[]): Enumerable; /** * Returns a new enumerable that contains only items containing a unique property value. * The default implementation returns an array regardless of the receiver type. diff --git a/types/ember/test/unsafe.ts b/types/ember/test/unsafe.ts new file mode 100644 index 0000000..f61ed0b --- /dev/null +++ b/types/ember/test/unsafe.ts @@ -0,0 +1,47 @@ +import Ember from 'ember'; + +declare module 'ember' { + namespace Ember { + function get(obj: any, key: string): any; + function set(obj: any, key: string, value: any): any; + function getProperties(obj: any, keys: K[]): Pick; + function getProperties(obj: any, ...keys: K[]): Pick; + function setProperties(obj: any, hash: Pick): Pick; + + interface CoreObject { + get(key: string): any; + set(key: string, value: any): any; + getProperties(keys: K[]): Pick; + getProperties(...keys: K[]): Pick; + setProperties(hash: Pick): Pick; + } + + interface Array { + getEach(key: string): any[]; + setEach(key: string, value: any): any; + mapBy(key: string): any[]; + filterBy(key: string, value?: any): T[]; + rejectBy(key: string, value?: any): T[]; + findBy(key: string, value: any): T | undefined; + isEvery(key: string, value?: boolean): boolean; + isAny(key: string, value?: boolean): boolean; + sortBy(...property: string[]): Enumerable; + } + } +} + +const obj = Ember.Object.create(); +obj.get('path.to.something'); +obj.set('path.to.something', 123); +obj.getProperties('path.to.something'); +obj.setProperties({ 'path.to.something': 123 }); +Ember.get(obj, 'path.to.something'); +Ember.set(obj, 'path.to.something', 123); +Ember.getProperties(obj, 'path.to.something'); +Ember.setProperties(obj, { 'path.to.something': 123 }); + +const arr = Ember.A(); +arr.mapBy('path.to.something'); +arr.filterBy('path.to.something'); +arr.isAny('path.to.something', true); +arr.isEvery('path.to.something'); diff --git a/types/ember/tsconfig.json b/types/ember/tsconfig.json index e628dfc..3778aeb 100644 --- a/types/ember/tsconfig.json +++ b/types/ember/tsconfig.json @@ -38,6 +38,7 @@ "test/utils.ts", "test/transition.ts", "test/router.ts", - "test/run.ts" + "test/run.ts", + "test/unsafe.ts" ] } From 25369d9dcd799ee284d43876d63c4d9e47853f8d Mon Sep 17 00:00:00 2001 From: Derek Wickern Date: Sat, 23 Sep 2017 12:43:53 -0700 Subject: [PATCH 2/5] fix sortBy return type --- types/ember/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/ember/index.d.ts b/types/ember/index.d.ts index 6d03b55..c186c43 100644 --- a/types/ember/index.d.ts +++ b/types/ember/index.d.ts @@ -922,7 +922,7 @@ export namespace Ember { * Converts the enumerable into an array and sorts by the keys * specified in the argument. */ - sortBy(...property: (keyof T)[]): Enumerable; + sortBy(...property: (keyof T)[]): T[]; /** * Returns a new enumerable that contains only items containing a unique property value. * The default implementation returns an array regardless of the receiver type. From 3aebd4ec22f7e9b0b78ec9869a2d9d3ac60d88b4 Mon Sep 17 00:00:00 2001 From: Derek Wickern Date: Sat, 23 Sep 2017 12:45:07 -0700 Subject: [PATCH 3/5] move unsafe typings to typed-ember/ember-unsafe-typings --- types/ember/test/unsafe.ts | 47 -------------------------------------- types/ember/tsconfig.json | 3 +-- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 types/ember/test/unsafe.ts diff --git a/types/ember/test/unsafe.ts b/types/ember/test/unsafe.ts deleted file mode 100644 index f61ed0b..0000000 --- a/types/ember/test/unsafe.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Ember from 'ember'; - -declare module 'ember' { - namespace Ember { - function get(obj: any, key: string): any; - function set(obj: any, key: string, value: any): any; - function getProperties(obj: any, keys: K[]): Pick; - function getProperties(obj: any, ...keys: K[]): Pick; - function setProperties(obj: any, hash: Pick): Pick; - - interface CoreObject { - get(key: string): any; - set(key: string, value: any): any; - getProperties(keys: K[]): Pick; - getProperties(...keys: K[]): Pick; - setProperties(hash: Pick): Pick; - } - - interface Array { - getEach(key: string): any[]; - setEach(key: string, value: any): any; - mapBy(key: string): any[]; - filterBy(key: string, value?: any): T[]; - rejectBy(key: string, value?: any): T[]; - findBy(key: string, value: any): T | undefined; - isEvery(key: string, value?: boolean): boolean; - isAny(key: string, value?: boolean): boolean; - sortBy(...property: string[]): Enumerable; - } - } -} - -const obj = Ember.Object.create(); -obj.get('path.to.something'); -obj.set('path.to.something', 123); -obj.getProperties('path.to.something'); -obj.setProperties({ 'path.to.something': 123 }); -Ember.get(obj, 'path.to.something'); -Ember.set(obj, 'path.to.something', 123); -Ember.getProperties(obj, 'path.to.something'); -Ember.setProperties(obj, { 'path.to.something': 123 }); - -const arr = Ember.A(); -arr.mapBy('path.to.something'); -arr.filterBy('path.to.something'); -arr.isAny('path.to.something', true); -arr.isEvery('path.to.something'); diff --git a/types/ember/tsconfig.json b/types/ember/tsconfig.json index 3778aeb..e628dfc 100644 --- a/types/ember/tsconfig.json +++ b/types/ember/tsconfig.json @@ -38,7 +38,6 @@ "test/utils.ts", "test/transition.ts", "test/router.ts", - "test/run.ts", - "test/unsafe.ts" + "test/run.ts" ] } From 2949b7f70f0de52ff7d499303c0870a635bd8d39 Mon Sep 17 00:00:00 2001 From: Derek Wickern Date: Sat, 23 Sep 2017 12:50:37 -0700 Subject: [PATCH 4/5] fix tslint errors --- types/ember/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/ember/index.d.ts b/types/ember/index.d.ts index c186c43..dc08385 100644 --- a/types/ember/index.d.ts +++ b/types/ember/index.d.ts @@ -809,7 +809,7 @@ export namespace Ember { /** * Alias for `mapBy` */ - getEach(key: K): T[K][]; + getEach(key: K): GlobalArray; /** * Sets the value on the named property for each member. This is more * ergonomic than using other methods defined on this helper. If the object @@ -826,7 +826,7 @@ export namespace Ember { * Similar to map, this specialized function returns the value of the named * property on all items in the enumeration. */ - mapBy(key: K): T[K][]; + mapBy(key: K): GlobalArray; /** * Returns an array with all of the items in the enumeration that the passed * function returns true for. This method corresponds to `filter()` defined in @@ -922,7 +922,7 @@ export namespace Ember { * Converts the enumerable into an array and sorts by the keys * specified in the argument. */ - sortBy(...property: (keyof T)[]): T[]; + sortBy(...property: GlobalArray): T[]; /** * Returns a new enumerable that contains only items containing a unique property value. * The default implementation returns an array regardless of the receiver type. From a9f787192673933bcdcf106c82e64288ec8bcaa8 Mon Sep 17 00:00:00 2001 From: Derek Wickern Date: Sat, 23 Sep 2017 12:50:58 -0700 Subject: [PATCH 5/5] fix test failures due to stricter array combinators --- types/ember/test/array.ts | 2 +- types/ember/test/ember-tests.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/ember/test/array.ts b/types/ember/test/array.ts index 21e6ac2..c4c79b5 100644 --- a/types/ember/test/array.ts +++ b/types/ember/test/array.ts @@ -21,7 +21,7 @@ assertType(people.get('[]')); assertType(people.get('[]').get('firstObject')); assertType(people.mapBy('isHappy')); -assertType(people.mapBy('name.length')); +assertType(people.mapBy('name').mapBy('length')); const last = people.get('lastObject'); if (last) { diff --git a/types/ember/test/ember-tests.ts b/types/ember/test/ember-tests.ts index 0ff2f4e..a7fb4f8 100644 --- a/types/ember/test/ember-tests.ts +++ b/types/ember/test/ember-tests.ts @@ -133,7 +133,7 @@ const people = Ember.A([ ]); people.invoke('sayHello'); -const arr = Ember.A([Ember.Object.create(), Ember.Object.create()]); +const arr = Ember.A([Person2.create(), Person2.create()]); arr.setEach('name', 'unknown'); arr.getEach('name');