From 14db1e4e2c597ba40b01d1668c360dadf7a3690b Mon Sep 17 00:00:00 2001 From: Dan Freeman Date: Fri, 3 May 2019 19:05:01 +0200 Subject: [PATCH] [ember] all computed property descriptors are also PropertyDecorators (#35114) --- types/ember__controller/index.d.ts | 4 +- types/ember__object/-private/types.d.ts | 21 ++++++- types/ember__object/computed.d.ts | 77 +++++++++++++------------ types/ember__object/index.d.ts | 2 +- types/ember__object/test/octane.ts | 17 ++++++ types/ember__service/index.d.ts | 4 +- 6 files changed, 81 insertions(+), 44 deletions(-) diff --git a/types/ember__controller/index.d.ts b/types/ember__controller/index.d.ts index 5071811a85..efb6aabb00 100644 --- a/types/ember__controller/index.d.ts +++ b/types/ember__controller/index.d.ts @@ -31,10 +31,10 @@ export interface ControllerMixin extends ActionHandler { export const ControllerMixin: Mixin; // tslint:disable-next-line:no-empty-interface export default class Controller extends EmberObject.extend(ControllerMixin) {} -export function inject(): ComputedProperty & PropertyDecorator; +export function inject(): ComputedProperty; export function inject( name: K -): ComputedProperty & PropertyDecorator; +): ComputedProperty; export function inject(target: object, propertyKey: string | symbol): void; // A type registry for Ember `Controller`s. Meant to be declaration-merged diff --git a/types/ember__object/-private/types.d.ts b/types/ember__object/-private/types.d.ts index cd8d713152..02a599e71a 100644 --- a/types/ember__object/-private/types.d.ts +++ b/types/ember__object/-private/types.d.ts @@ -18,6 +18,23 @@ export type ExtractPropertyNamesOfType = { export type Fix = { [K in keyof T]: T[K] }; +/** + * Used to capture type information about a computed property, both + * the type of its value and (if it differs) the type its setter expects + * to receive. + * + * Note that this is intentionally a `class` and not a `type` or + * `interface` so that we can sneak in private fields that capture + * type info for the computed property without impacting the + * user-visible type. + */ +export class ComputedPropertyMarker { + // Necessary in order to avoid losing type information + // see: https://github.com/typed-ember/ember-cli-typescript/issues/246#issuecomment-414812013 + private ______getType: Get; + private ______setType: Set; +} + /** * Used to infer the type of ember classes of type `T`. * @@ -56,7 +73,7 @@ export type MixinOrLiteral = Mixin | T; /** * Deconstructs computed properties into the types which would be returned by `.get()`. */ -export type UnwrapComputedPropertyGetter = T extends ComputedProperty< +export type UnwrapComputedPropertyGetter = T extends ComputedPropertyMarker< infer U, any > @@ -66,7 +83,7 @@ export type UnwrapComputedPropertyGetters = { [P in keyof T]: UnwrapComputedPropertyGetter }; -export type UnwrapComputedPropertySetter = T extends ComputedProperty< +export type UnwrapComputedPropertySetter = T extends ComputedPropertyMarker< any, infer V > diff --git a/types/ember__object/computed.d.ts b/types/ember__object/computed.d.ts index 8053c5a82a..69aa74879e 100644 --- a/types/ember__object/computed.d.ts +++ b/types/ember__object/computed.d.ts @@ -1,4 +1,5 @@ import { computed } from "@ember/object"; +import { ComputedPropertyMarker } from "./-private/types"; /** * A computed property transforms an objects function into a property. @@ -7,10 +8,6 @@ import { computed } from "@ember/object"; * This will force the cached result to be recomputed if the dependencies are modified. */ export default class ComputedProperty { - // Necessary in order to avoid losing type information - // see: https://github.com/typed-ember/ember-cli-typescript/issues/246#issuecomment-414812013 - private ______getType: Get; - private ______setType: Set; /** * Call on a computed property to set it into non-cached mode. When in this * mode the computed property will not automatically cache the return value. @@ -36,6 +33,12 @@ export default class ComputedProperty { meta(): {}; } +// Computed property definitions also act as property decorators, including those +// returned from "macros" in third-party code. We additionally include a marker +// interface that we use in `UnwrapComputedProperty{G,S}etters`. +export default interface ComputedProperty + extends PropertyDecorator, ComputedPropertyMarker {} + /** * Creates a new property that is an alias for another property * on an object. Calls to `get` or `set` this property behave as @@ -43,21 +46,21 @@ export default class ComputedProperty { */ export function alias( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that performs a logical `and` on the * original values for the provided dependent properties. */ export function and( ...dependentKeys: string[] -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that converts the provided dependent property * into a boolean value. */ export function bool( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns the array of values @@ -65,7 +68,7 @@ export function bool( */ export function collect( ...dependentKeys: string[] -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Creates a new property that is an alias for another property @@ -76,14 +79,14 @@ export function collect( export function deprecatingAlias( dependentKey: string, options: { id: string; until: string } -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options */ export function deprecatingAlias( dependentKey: string, options?: { id?: string; until?: string } -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the value of the dependent @@ -91,7 +94,7 @@ export function deprecatingAlias( */ export function empty( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the provided dependent property * is equal to the given value. @@ -99,7 +102,7 @@ export function empty( export function equal( dependentKey: string, value: any -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Expands `pattern`, invoking `callback` for each expansion. */ @@ -114,7 +117,7 @@ export function expandProperties( export function filter( dependentKey: string, callback: (value: any, index: number, array: any[]) => boolean -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Filters the array by the property and value @@ -123,7 +126,7 @@ export function filterBy( dependentKey: string, propertyKey: string, value?: any -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the provided dependent property @@ -132,7 +135,7 @@ export function filterBy( export function gt( dependentKey: string, value: number -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the provided dependent property * is greater than or equal to the provided value. @@ -140,7 +143,7 @@ export function gt( export function gte( dependentKey: string, value: number -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the elements @@ -148,7 +151,7 @@ export function gte( */ export function intersect( ...propertyKeys: string[] -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the provided dependent property @@ -157,7 +160,7 @@ export function intersect( export function lt( dependentKey: string, value: number -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the provided dependent property * is less than or equal to the provided value. @@ -165,7 +168,7 @@ export function lt( export function lte( dependentKey: string, value: number -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Returns an array mapped via the callback @@ -173,7 +176,7 @@ export function lte( export function map( dependentKey: string, callback: (value: any, index: number, array: any[]) => U -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Returns an array mapped to the specified key. @@ -181,7 +184,7 @@ export function map( export function mapBy( dependentKey: string, propertyKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which matches the original value for the @@ -191,7 +194,7 @@ export function mapBy( export function match( dependentKey: string, regexp: RegExp -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that calculates the maximum value in the @@ -200,7 +203,7 @@ export function match( */ export function max( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that calculates the minimum value in the @@ -209,7 +212,7 @@ export function max( */ export function min( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the value of the dependent @@ -218,21 +221,21 @@ export function min( */ export function none( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns the inverse boolean value * of the original value for the dependent property. */ export function not( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns true if the value of the dependent * property is NOT null, an empty string, empty array, or empty function. */ export function notEmpty( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Where `computed.alias` aliases `get` and `set`, and allows for bidirectional * data flow, `computed.oneWay` only provides an aliased `get`. The `set` will @@ -242,14 +245,14 @@ export function notEmpty( */ export function oneWay( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which performs a logical `or` on the * original values for the provided dependent properties. */ export function or( ...dependentKeys: string[] -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides * a readOnly one way binding. Very often when using `computed.oneWay` one does @@ -257,14 +260,14 @@ export function or( */ export function readOnly( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * This is a more semantically meaningful alias of `computed.oneWay`, * whose name is somewhat ambiguous as to which direction the data flows. */ export function reads( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the @@ -274,7 +277,7 @@ export function reads( export function setDiff( setAProperty: string, setBProperty: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the @@ -284,14 +287,14 @@ export function setDiff( export function sort( itemsKey: string, sortDefinition: string | ((itemA: any, itemB: any) => number) -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property that returns the sum of the values * in the dependent array. */ export function sum( dependentKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the unique @@ -299,7 +302,7 @@ export function sum( */ export function uniq( propertyKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the unique @@ -307,7 +310,7 @@ export function uniq( */ export function union( ...propertyKeys: string[] -): ComputedProperty & PropertyDecorator; +): ComputedProperty; /** * A computed property which returns a new array with all the unique @@ -316,4 +319,4 @@ export function union( export function uniqBy( dependentKey: string, propertyKey: string -): ComputedProperty & PropertyDecorator; +): ComputedProperty; diff --git a/types/ember__object/index.d.ts b/types/ember__object/index.d.ts index fb5a76efd2..ead719d70e 100644 --- a/types/ember__object/index.d.ts +++ b/types/ember__object/index.d.ts @@ -22,7 +22,7 @@ import ComputedProperty, * as ComputedNamespace from "@ember/object/computed"; * see the documentation for each of these. */ export default class EmberObject extends CoreObject.extend(Observable) {} -declare function computed(...deps: string[]): (target: object, propertyKey: string) => void; +declare function computed(...deps: string[]): MethodDecorator; declare function computed( cb: ComputedPropertyCallback ): ComputedProperty; diff --git a/types/ember__object/test/octane.ts b/types/ember__object/test/octane.ts index d475cab777..27f0a95522 100644 --- a/types/ember__object/test/octane.ts +++ b/types/ember__object/test/octane.ts @@ -34,6 +34,12 @@ import { uniqBy } from "@ember/object/computed"; +function customMacro(message: string) { + return computed(() => { + return [message, message]; + }); +} + // Native class syntax class Foo extends EmberObject { firstName: string; @@ -47,6 +53,17 @@ class Foo extends EmberObject { return `${this.firstName} ${this.lastName}`; } + @computed("firstName", "lastName") // $ExpectError + badFullName: string; + + @computed("fullName", function(this: Foo) { + return this.fullName.toUpperCase(); + }) + bigFullName: string; + + @customMacro('hi') + hiTwice: string[]; + @action foo() {} } diff --git a/types/ember__service/index.d.ts b/types/ember__service/index.d.ts index 416d705940..bf5b9f6214 100644 --- a/types/ember__service/index.d.ts +++ b/types/ember__service/index.d.ts @@ -12,9 +12,9 @@ export default class Service extends EmberObject {} * Creates a property that lazily looks up a service in the container. There * are no restrictions as to what objects a service can be injected into. */ -export function inject(): PropertyDecorator & ComputedProperty; // @inject() foo, foo: inject() +export function inject(): ComputedProperty; // @inject() foo, foo: inject() export function inject(target: object, propertyKey: string | symbol): void; // @inject foo -export function inject(name: K): PropertyDecorator & ComputedProperty; // @inject('store') foo @inject() foo +export function inject(name: K): ComputedProperty; // @inject('store') foo @inject() foo // A type registry for Ember `Service`s. Meant to be declaration-merged so // string lookups resolve to the correct type.