[@types/ember] rewrite computed property unwrapping to use infer#28282
[@types/ember] rewrite computed property unwrapping to use infer#28282minestarks merged 16 commits intoDefinitelyTyped:masterfrom
Conversation
f0d4d5a to
e3b58b6
Compare
|
@mike-north Thank you for submitting this PR! Because this is a new definition, a DefinitelyTyped maintainer will be reviewing this PR in the next few days once the Travis CI build passes. In the meantime, if the build fails or a merge conflict occurs, I'll let you know. Have a nice day! |
|
@mike-north Unfortunately, this pull request currently has a merge conflict 😥. Please update your PR branch to be up-to-date with respect to master. Have a nice day! |
ac295a0 to
5854354
Compare
5854354 to
b544e32
Compare
|
To reviewers Literally 99% lines changed in this pull request are due to cutting several major releases. The 1% that involves changes to types (as opposed to just copying files or breaking them into multiple files) is all in one commit. This is the actual bug fix. Everything else is to ensure we release this properly and don't break anyone's app. There are two reasons this PR cannot be safely split up:
declare module 'ember-data' {
import Ember from 'ember'; // <-- this wasn't enough for ts-publisher
// to detect ember/v2 as "used"
export emberDataStuff;
}The easiest way to solve this was breaking our single import Ember from 'ember'; // <-- ts-publisher was happy with this refactor
export emberDataStuff; |
|
This seems to fix typed-ember/ember-cli-typescript#226 as a side effect. Maybe you could add that issue's reproduction to the test suite. |
|
@dwickern I'd prefer to do that in a second pass, in case it's only partially fixed, or only fixed in a certain range of TS versions (i.e., 3+). Glad to hear that an extra issue may have accidentally been resolved! |
dfreeman
left a comment
There was a problem hiding this comment.
Thanks for all your work fixing this and defining a versioning strategy! There are a couple of places where I think the change in strategy for unwrapping computeds means the signatures need to be reworked slightly, but overall this looks great.
types/ember/index.d.ts
Outdated
| static create<Instance, Args, T1 extends EmberInstanceArguments<Args>>( | ||
| this: EmberClassConstructor<Instance & ComputedPropertyGetters<Args>>, | ||
| this: EmberClassConstructor<UnwrapComputedPropertyGetters<Args> & Instance>, | ||
| arg1: T1 & ThisType<Fix<T1 & Instance>> |
There was a problem hiding this comment.
I never fully wrapped my head around how the previous approach was working here, but I think this is backwards now? It looks like the existing tests for create aren't super extensive, but given how the Getters wrapper moved from the source to the return value in e.g. the signature of get, I think I would expect a similar change here?
// Before
function get<T, K extends keyof T>(obj: ComputedPropertyGetters<T>, key: K): T[K];
// After
function get<T, K extends keyof T>(obj: T, key: K): UnwrapComputedPropertyGetter<T[K]>;I.e. the instance type of the this value here can have ComputedProperty properties, but arg1 shouldn't, right?
There was a problem hiding this comment.
With TS 2.8 I think we can declare it more simply:
static create<Class extends typeof Ember.CoreObject, Arg1>(
this: Class,
arg1: Arg1 & ThisType<Arg1 & InstanceType<Class>>
): InstanceType<Class> & Arg1;I'll call out a couple of things that are missing compared to the original declaration.
-
EmberInstanceArguments<T>verified that the arguments passed tocreate()matched the types on the class. E.g.Object.extend({ x: 42 }).create({ x: 'foo' })would not typecheck (although it does work at runtime--xis a string). -
Fix<T>worked around Implement typed computed property access typed-ember/ember-typings#32. I suspect it's no longer needed.
There was a problem hiding this comment.
I think it would be useful for us to take a pass at create and the associated code as a follow-on effort and see what we can clean up there (as well as fleshing out the test coverage).
In the meantime, though, hopefully there's a low-lift way to keep things working roughly as-is with UnwrapComputedPropertyGetters to get this PR landed?
There was a problem hiding this comment.
In the future I also want us to stop &ing InstanceType<Class> & Arg1. I don't believe collisions are handled properly.
{ // InstanceType<Class>
foo: string
} & { // .create args
foo: number
} = {
foo: string & number
}
it should be
{
foo: number
}Maybe something like
type Mix<A, B> = B & Pick<A, Exclude<keyof A, keyof B>>;There was a problem hiding this comment.
Agreed that &ing together the types is incorrect there (and doubtless there are other places we're doing that as well that should be fixed), but shouldn't code like this be a type error regardless?
const MyClass = EmberObject.extend({
foo: 'hello'
});
MyClass.create({ foo: 5 });Seems like (roughly) the moral equivalent of
class MyClass {
constructor(public foo: string = 'hello') {}
}
new MyClass(5);There was a problem hiding this comment.
@mike-north I'm not positive I'm in a better position than you are w.r.t. the current mechanics of create, but I'm happy to take a pass later today and see if I can get the tests passing if @dwickern doesn't beat me to it.
There was a problem hiding this comment.
We can add the EmberInstanceArguments constraint back to my previous example.
static create<
Class extends typeof Ember.CoreObject,
Arg1 extends EmberInstanceArguments<UnwrapComputedPropertySetters<InstanceType<Class>>>
>(
this: Class,
arg1: Arg1 & ThisType<Arg1 & InstanceType<Class>>
): InstanceType<Class> & Arg1;Should Person.create({ fullName: 'string' }) be allowed?
There was a problem hiding this comment.
Unless it complicates the typing significantly, I believe it should, as long as the Set type on the computed is compatible. It's a little weird in this specific example since the computed isn't actually set up to work with set, but that's not currently something we try to capture in the type system (though authors could do that explicitly with Computed<string, never>)
There was a problem hiding this comment.
I'll give you both commit access to my fork. Unfortunately, I'm out of time to spend on this for a few days
There was a problem hiding this comment.
The Ember.Object.create issue is now resolved. Positive and negative test cases added to reflect this for 0-4 arguments passed to .create
types/ember/index.d.ts
Outdated
| * This mixin provides properties and property observing functionality, core features of the Ember object model. | ||
| */ | ||
| interface Observable { | ||
| thisType: this; |
There was a problem hiding this comment.
What is this for? If it's needed, could you add a comment describing why?
types/ember/index.d.ts
Outdated
| obj: ComputedPropertyGetters<T>, | ||
| obj: UnwrapComputedPropertyGetters<T>, | ||
| key: K | ||
| ): T[K] | undefined; |
There was a problem hiding this comment.
UnwrapComputedPropertyGetters should be wrapping the return type here rather than obj, shouldn't it?
types/ember/index.d.ts
Outdated
| */ | ||
| function set<T, K extends keyof T, V extends T[K]>( | ||
| obj: ComputedPropertySetters<T>, | ||
| obj: UnwrapComputedPropertySetters<T>, |
There was a problem hiding this comment.
I think this is another inversion.
function set<T, K extends keyof T, V extends UnwrapComputedPropertySetter<T[K]>>(
obj: t,
key: K,
value: V
): V;There was a problem hiding this comment.
Can you guys contribute some test cases that validate the correct behavior?
There was a problem hiding this comment.
@dfreeman I don't believe that this is correct. Current tests fail w/ your suggested implementation, and pass with the current code
There was a problem hiding this comment.
It seems to work either way 🤷♂️
Would it be more correct for set to return the get type?
function set<T, K extends keyof T>(
obj: T,
key: K,
value: UnwrapComputedPropertySetter<T[K]>
): UnwrapComputedPropertyGetter<T[K]>;d36019c to
b8afd9b
Compare
|
@mike-north The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! |
739e988 to
4f3dc57
Compare
4f3dc57 to
d99587f
Compare
fix: remove unused code fix: consistent treatment of zero-argument Ember.Object.create
d99587f to
121bbc2
Compare
dfreeman
left a comment
There was a problem hiding this comment.
LGTM for the packages with my fingerprints on them. Thanks Mike!
| hello() { return 'world'; } | ||
| protected bar() { return 'bar'; } // $ExpectError | ||
| private baz() { return 'baz'; } | ||
| } |
| const p4 = new PersonWithNumberName(); | ||
| // assertType<number>(p4.get('fullName')); | ||
|
|
||
| // assertType<Ember.ComputedProperty<string, string>>(p4.fullName); // $ExpectError |
There was a problem hiding this comment.
Is there a story on these commented type assertions? Based on the assertions in test/create.ts I'd expect p4.get('fullName') and p4.fullName both to have type number, but I'm not sure what the aim is here.
There was a problem hiding this comment.
The line you're commenting on is the interesting. Currently (and in prior releases of these types) the type of fullName is string && ComputedProperty<string, string>.
This is not an accurate representation of what's happening with Ember.Object.create and Ember.Object.extend -- these methods align with the behavior of Object.assign where target object properties are usually replaced.
In this case, the value should just be string. We'll resolve this when we apply the change described here as a future enhancement.
|
A definition owner has approved this PR ⭐️. A maintainer will merge this PR shortly. If it shouldn't be merged yet, please leave a comment saying so and we'll wait. Thank you for your contribution to DefinitelyTyped! |
Please fill in this template.
npm test.)npm run lint package-name(ortscif notslint.jsonis present).A breaking change was introduced in recent typescript 3.1 nightly builds, likely due to changes in how arrays interact with mapping types. Unfortunately,
@types/emberrelied on the old behavior (removed by microsoft/TypeScript#26063) in order to infer the type of a computed property.DefinitelyTyped/types/ember/index.d.ts
Lines 33 to 34 in b77ae9d
(in the above example, imagine that
Tis an array, and you'll understand why our types forarray.firstObjectbroke)@dwickern suggested (and I agree) that we shift toward using
inferfor extracting the wrapped type, which unfortunately means dropping support for all TypeScript versions below 2.8 (this is consistent with our policy of supporting at least the latest two TS releases )I'm deliberately not adding additional tests, to ensure that this fix takes care of the tests broken in master, and keeps the rest passing with no additional changes required.
Related: microsoft/TypeScript#26120
Fixes typed-ember/ember-cli-typescript#246
Fixes typed-ember/ember-cli-typescript#226