Skip to content
This repository has been archived by the owner on Jul 6, 2018. It is now read-only.

Add a type parameter to Enumerable and friends #26

Merged
merged 2 commits into from
Sep 4, 2017

Conversation

dfreeman
Copy link
Member

This change makes Ember.Enumerable and its descendants polymorphic, allowing for better type safety in their properties and methods.

@dfreeman
Copy link
Member Author

For whatever reason the tests were passing locally, but based on Travis and microsoft/TypeScript#17829 I may need to rethink this slightly.

/**
An ArrayProxy wraps any other object that implements Ember.Array and/or Ember.MutableArray,
forwarding all requests. This makes it very useful for a number of binding use cases or other cases
where being able to swap out the underlying array is useful.
**/
class ArrayProxy extends Object.extend(MutableArray) {
content: NativeArray;
class ArrayProxy<T> extends Object.extend(MutableArray as Mixin<MutableArray<T>>) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do the class/interface merging trick?

interface ArrayProxy<T> extends MutableArray<T> {}
class ArrayProxy<T> extends Object.extend(MutableArray) {}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but we'd need to do something like this to avoid a conflicting merge between MutableArray<T> and MutableArray<any>:

interface ArrayProxy<T> extends MutableArray<T> {}
class ArrayProxy<T> extends Object.extend(MutableArray as {}) {}

Since this is just a declaration file, we could actually get away without the .extend() call at all, but real code would need it to pick up the actual mixin implementation.

This is super ugly, but given that type variables apparently aren't in scope in base class expressions, we're pretty restricted. I'm not sure I have a better alternative to propose.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing a quick code search on EmberObserver, code using the enumerable family of mixins only appears in a handful of places, and it tends to be in fairly low-level "plumbing" classes.

Given that, it may not be the end of the world that getting the proper typing takes some gymnastics, but it sure seems unfortunate :/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as generic mixins aren't a common thing, this should be OK

@dwickern
Copy link
Collaborator

dwickern commented Sep 4, 2017

This is a good start. Going forward I think we can strengthen the types a bit.

I noticed you have to overloads for mapBy:

mapBy<K extends keyof T>(key: K): GlobalArray<T[K]>;
mapBy(key: string): any[];

Maybe it's worthwhile to separate out these "unsafe" methods which deal with path strings. They could be opt-in like the prototype extensions. We could also offer a typescript-specific API on top of ember which solves this. (e.g. Array.mapByPath(k1, k2, ..., kn))

@dwickern
Copy link
Collaborator

dwickern commented Sep 4, 2017

We could work around the issue of extending generic mixins using ES6 classes

interface MutableArray<T> { ... }
class MutableArray<T> extends Mixin<MutableArray<T>> {
  private constructor();
}

That's slightly different from what we discussed before about getting rid of the Mixin type parameter entirely. I don't feel too strongly either way.

@dwickern dwickern merged commit b11bb5f into typed-ember:master Sep 4, 2017
@dfreeman
Copy link
Member Author

dfreeman commented Sep 4, 2017

Maybe it's worthwhile to separate out these "unsafe" methods which deal with path strings. They could be opt-in like the prototype extensions. We could also offer a typescript-specific API on top of ember which solves this. (e.g. Array.mapByPath(k1, k2, ..., kn))

Yeah, my thought with the overload was to provide some inference where possible without totally breaking things for the nested path use case. I agree it would be interesting to try and consider API patterns needed across the board to improve typeability for operations on key paths — things like get(), dependent keys for computeds, etc.

@dfreeman
Copy link
Member Author

dfreeman commented Sep 4, 2017

We could work around the issue of extending generic mixins using ES6 classes

We could, but it only really solves the problem for declaration files—since actual Ember mixins don't work that way, there would always be a disconnect between the typings and the implementation. Like you I don't have a super strong opinion, but my leaning is probably to stick with the status quo until we start seeing more generic mixins pop up.

There's been some talk on the subject of mixins with ES classes in the #topic-es-decorators channel in Slack, but no strong conclusions yet. Decorators in TS unfortunately don't have an impact on the type of their target at this point, though, which is unfortunate.

@dfreeman dfreeman deleted the polymorphic-enumerables branch September 4, 2017 23:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants