1

I have a method in a class whose call signature is:

async where(
    prop: K & string,
    value: PropType<T, K> | [IComparisonOperator, PropType<T, K>],
    options: IDexieListOptions<T> = {}
  ) { ...}

The class's definition defines both T and K as:

class MyClass<T extends Model, K extends keyof T> { ... }

and the PropType type is simply this:

type PropType<TObj, TProp extends keyof TObj> = TObj[TProp]

In another method of the same class I try to use where like so:

  async since(datetime: epoch) {
    return this.where("lastUpdated", [">", datetime]);
  }

the lastUpdated property is a valid property of Model which <T> extends and of course it is also a "string" so it would appear to meet the signature for where() but instead I get this errror:

error msg

'"lastUpdated"' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string | number | symbol'.

Does anyone know how to get around this?


The Model class -- which I should have included to start with -- is defined as such:

export class Model {
  public id?: string;
  public lastUpdated?: number;
  public createdAt?: number;
}

I did wonder if the fact that lastUpdated is optional was a contributing factor but even making it required seems to make no difference.

ken
  • 8,763
  • 11
  • 72
  • 133
  • It doesn't meet the signature for `where()` because an instance of `MyClass` has a *specifc* `K` associated with it. Perhaps you want `MyClass` to only be generic in `T`, and then have `where()` itself be a generic function in `K`, like `class MyClass { where( prop: K, ... ) {...} }`? But without a [mcve] I'm not sure that's what you're looking for. Consider making some example code that can be dropped into a standalone IDE like [The Playground](https://www.typescriptlang.org/play/) to demonstrate the issue. – jcalz Jan 07 '20 at 19:52
  • Example: is [this](https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgLIHsAmEA2yDeAsAFDLI5wDOYAqgA6ZySYBcyIArgLYBG0ANCTIx06AEJwobalFABzANwkAviRIIKlSmgCeAYU2UAPABVkEAB6QQmbRmw4AfASHIqOkAmQB3ABbQIIwBpcysIG20AawgddBhkE0cAClcyOih0OjYQgDJkGXlBUjJkADc4HA4INgBtOBAdfgSaoIBdVqKyAEoCZFViV3dPfNAkJMZIMGAuavZuPigeomKyKAgwDigQZDBfYEoAOj8ApIAiTVoGJghMU6aa08c75An16YhWrqVi-uVkEiAA) what you're looking for? – jcalz Jan 07 '20 at 19:54
  • I should have mentioned that the `Model` class which is referred to has `lastUpdated: number` defined (as you illustrated in your example @jcalz. I would have expected that this would mean that EVERY instance of T would have a `K` where `lastUpdated` would be defined as a number. – ken Jan 08 '20 at 20:00
  • I have updated the question to include details about the `Model` class – ken Jan 08 '20 at 20:07
  • 1
    Have you checked if my example code would work for you? `K` is a type variable whose scope is determined by where it's declared. If `K` is declared on `MyClass`, then you can't change it when you call `where()`; it's fixed. Imagine a pure JS function like this: `const badClass = (t, k) => { const where = () => { }; const since = () => where("lastupdated"); /*oops*/}`. That doesn't work because `where` doesn't take a `k`; it's fixed by the time you call `where()`. Compare to `const goodClass = (t) => { const where = (k) => { }; const since = () => where("lastupdated"); }` where it works. – jcalz Jan 08 '20 at 20:17

1 Answers1

2

The problem is that your class define the second generic parameter K to be any subtype of keyof T but you are using a concrete subtype "lastUpdated" in the since() function when T isn't know yet. This isn't allowed as explained in detail here: https://stackoverflow.com/a/59363875/1628117

The solution of @jcalz in the above comments is an approach to solve this problem by letting the where function itself decide what a suitable subtype for keyof T is when invoked.

class MyClass<T extends Model> {

  async where<K extends keyof T>(
    prop: K, ...
  ) { }

  ...

}
iY1NQ
  • 2,308
  • 16
  • 18
  • But _part_ of `T` is known ... it is always an extension of the `Model` class which includes the `lastUpdated` property. I have updated the question to include this detail. – ken Jan 08 '20 at 20:01
  • Oh wait, i get it. This necessitates that the definition of `` be at the method level and not the class level. Ok, yes this works. Many thanks @iYNQ and @jcalz. – ken Jan 08 '20 at 20:13