Feature: Update @computedFrom to support typing
I'm submitting a feature request
- Library Version: 1.7.3
Please tell us about your environment:
-
Operating System: Windows 10
-
Node Version: v8.11.1
-
Yarn Version: 1.5.1
-
Language: TypeScript 3.0
Current behavior: We do not get any type safety when using @computedFrom. A few blogs give users a method for extending @computedFrom manually, but why not just make this an upstream feature we can use?
What is the expected behavior? That @computedFrom can give us type safety out-of-the-box.
https://medium.com/tech-effectory/creating-a-typed-version-of-aurelias-computedfrom-decorator-with-typescript-27219651ecee
Example:
import {computedFrom as originalComputedFrom} from "aurelia-framework";
export function computedFrom<T>(...rest: Array<keyof T>) {
return originalComputedFrom(...rest);
}
import {computedFrom} from './typedcomputedfrom';
export class App {
public foo = "a";
public bar = "b";
@computedFrom<App>("foo")
public get foobar () {
return `${this.foo} ${this.bar}`;
}
}
What is the motivation / use case for changing the behavior?
- More bugs can be caught at compile-time, this will reduce chance of "typo" mistakes when using @computedFrom
- Rather than individual projects adopt this pattern and import a "fake" version of @computedFrom across their codebase, it's standard.
@silbinarywolf Awesome issue. It would be nice if you could help update the typings here https://github.com/aurelia/binding/blob/master/src/aurelia-binding.d.ts#L877
I experimented locally with putting this in as so:
node_modules\aurelia-binding\dist\aurelia-binding.d.ts
/**
* Decorator: Indicates that the decorated property is computed from other properties.
* @param propertyNames The names of the properties the decorated property is computed from. Simple property names, not expressions.
*/
export declare function computedFrom(...propertyNames: string[]): any;
/**
* Decorator: Indicates that the decorated property is computed from other properties. Adds compile-time type safety to @computedFrom.
* @param propertyNames The names of the properties the decorated property is computed from. Simple property names, not expressions.
*/
export declare function computedFrom<T>(...propertyNames: Array<keyof T>);
However, what I found was that keyof doesn't work with private members. So now Im not so sure on this feature as I wouldn't want inexperienced programmers to think they should make various members "public" because that gives less errors.
There's a thread discussing the problem in detail here: https://github.com/Microsoft/TypeScript/issues/13543
Oh nice find. We can come back to this later. One thing we need is to ensure backward compatibility, so it would be
export declare function computedFrom<T = any>(...propertyNames: Array<keyof T>);
Well, the way I've posted above is backwards compatible as well, it just has two-definitions, one without the polymorphic parameter and one with it :)
If your version has no backwards compatibility breaks, then I'd go with that :)
Nice. I didn't notice that. Thought it was for diffing
I also want to point out that computedFrom also supports sub properties like @computedFrom('sub.value'). It should be supported by the typing if you want to go that way.
The problem with private members can be solved by using an expression instead of keyof.
E.g.
class Example {
private secret: string;
@computedFrom<Example>(x => x.hidden)
public get wisperedSecret(): string { return `Psst! ${this.secret}`; }
}
As long as the expression is typed inside the scope of the class itself, like above, you can access private members as expected.
I'm already using something similar today, where I use a Proxy<> to walk the expression chain to turn it into a string, so it also works with x => x.foo.bar.baz for example.
@AnorZaken Looks great! 🚀 Do you mind sharing the solution using Proxy<>?