Skip to content

Feature: Update @computedFrom to support typing #727

Description

@silbinarywolf

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.

Activity

  1. changed the title [-]Feature: [/-] [+]Feature: Update @computedFrom to support typing[/+] on Nov 26, 2018
  2. bigopon commented on Nov 26, 2018

    @bigopon
    Member

    @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

  3. silbinarywolf commented on Nov 27, 2018

    @silbinarywolf
    Author

    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.

  4. silbinarywolf commented on Nov 27, 2018

    @silbinarywolf
    Author

    There's a thread discussing the problem in detail here:
    microsoft/TypeScript#13543

  5. bigopon commented on Nov 27, 2018

    @bigopon
    Member

    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>);
  6. silbinarywolf commented on Nov 27, 2018

    @silbinarywolf
    Author

    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 :)

  7. bigopon commented on Nov 27, 2018

    @bigopon
    Member

    Nice. I didn't notice that. Thought it was for diffing

  8. qraynaud commented on Sep 8, 2019

    @qraynaud

    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.

  9. AnorZaken commented on Jul 4, 2022

    @AnorZaken

    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.

  10. anton-gustafsson commented on Jan 23, 2024

    @anton-gustafsson
    Sponsor

    @AnorZaken Looks great! 🚀 Do you mind sharing the solution using Proxy<>?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions