ember-cli-typescript icon indicating copy to clipboard operation
ember-cli-typescript copied to clipboard

[ember-data] references API is not compatible with [email protected] and higher

Open ro0gr opened this issue 3 years ago • 5 comments

The problem appears when you try to use this.belongsTo( or this.hasMany( in scope of a model method or accesor. In this case this is broken, and it complains about the invalid invocation, until this type is explicitly specified in the method signature.

Unfortunately, an explicit this workaround doesn't work for accesors in [email protected] and higher, since they forbidden to specify this on getters/setters, see https://github.com/microsoft/TypeScript/issues/39254#issuecomment-649831793.

Which package(s) does this problem pertain to?

  • [ ] @types/ember
  • [ ] @types/ember__string
  • [ ] @types/ember__polyfills
  • [ ] @types/ember__object
  • [ ] @types/ember__utils
  • [ ] @types/ember__array
  • [ ] @types/ember__engine
  • [ ] @types/ember__debug
  • [ ] @types/ember__runloop
  • [ ] @types/ember__error
  • [ ] @types/ember__controller
  • [ ] @types/ember__component
  • [ ] @types/ember__routing
  • [ ] @types/ember__application
  • [ ] @types/ember__test
  • [ ] @types/ember__test-helpers
  • [ ] @types/ember__service
  • [x] @types/ember-data
  • [ ] @types/rsvp
  • [ ] Other
  • [ ] I don't know

What are instructions we can follow to reproduce the issue?


export default class User extends Model {
  @belongsTo("user")
  friend!: AsyncBelongsTo<User>;

  // `this: User` leads to the following compilation error:
  // > get' and 'set' accessors cannot declare 'this' parameters.t
  get friendId(this: User) {
    // if don't specify `this`, it'd fail on the next line
    return this.belongsTo("friend").value()?.id;
  }
}
Reproduction Case

Link: https://codesandbox.io/s/my-app-forked-zqghf?file=/app/models/user.ts

Now about that bug. What did you expect to see?

I expect:

export default class User extends Model {
  @belongsTo("user")
  friend!: AsyncBelongsTo<User>;

  get friendId() {
    return this.belongsTo("friend").value()?.id;
  }
}

to work w/o a need for explicit this and compilation errors.

What happened instead?

If remove an explicit this from the signature,

  return this.belongsTo("friend").value()?.id;

would lead to a compilation error:

Argument of type 'string' is not assignable to parameter of type 'Exclude<keyof this, "isEmpty" | "isLoading" | "isLoaded" | "hasDirtyAttributes" | "isSaving" | "isDeleted" | "isNew" | "isValid" | "dirtyType" | "isError" | "isReloading" | "id" | ... 41 more ... | "toString">'.ts(2345) Compilation error:

ro0gr avatar Mar 17 '21 21:03 ro0gr

A while ago, I did a PR(https://github.com/DefinitelyTyped/DefinitelyTyped/pull/42839), which fixed the belongsTo(, hasMany( return types. And as a side effect, it did also fix this corrupted this issue.

Unfortunately it has never been merged. Happy to reopen it.

ro0gr avatar Mar 17 '21 21:03 ro0gr

@ro0gr thanks for writing this up and sorry that PR ended up languishing. If you reopen it, I’ll help you get it landed!

chriskrycho avatar Oct 26 '21 16:10 chriskrycho

@chriskrycho thanks for the heads up! Glad to hear that.

Ok, I'll give it another try this weekend.

ro0gr avatar Oct 26 '21 18:10 ro0gr

A workaround is to use call and pass the reference as an argument, like such

return this.belongsTo.call(this, 'friend').value().id
// Or 
return this.belongsTo.call(this, 'friend').id()

ewal avatar Apr 05 '22 10:04 ewal

My workaround ended up being

  get friendId() {
    let inst = this as User;
    return this.belongsTo("friend").value()?.id;
  }

though I suspect I may be encountering a slightly separate issue

JarrettSpiker avatar Jul 25 '22 17:07 JarrettSpiker