1

This my setup:

Define properties

interface Props<T, X extends any[], Y extends any[]> {
  T?: T
  x?: <D extends T>(d: D, ...args: X) => number | Date | string
  y?: <D extends T>(d: D, ...args: Y) => number | Date | string
}

Define Component which consumes Props interface

class Component<T, X extends any[], Y extends any[]> {
  _props: Props<T, X, Y>

  constructor(props:Props<T, X, Y>){
    this._props = props
  }
}

Instantiate Component (I want the component to infer the generic types from props)

const comp = new Component({
  T:{a:10, b:20},
  x:(d, g:number)=> d.a
})

The problem I am facing is when I use the x (or y) property the following error will be printed

Type '<D extends { a: number; b: number; }>(d: D, g: number) => number' is not assignable to type '<D extends { a: number; b: number; }>(d: D, g: number) => string | number | Date'.
  Types of parameters 'd' and 'd' are incompatible.
    Type 'D' is not assignable to type 'D'. Two different types with this name exist, but they are unrelated.
      'D' is assignable to the constraint of type 'D', but 'D' could be instantiated with a different subtype of constraint '{ a: number; b: number; }'.
        Type '{ a: number; b: number; }' is not assignable to type 'D'.
          '{ a: number; b: number; }' is assignable to the constraint of type 'D', but 'D' could be instantiated with a different subtype of constraint '{ a: number; b: number; }'.

Here is the full code in Typescript Playground

what I want is to infer the rest parameters type and use it later, so please if anybody has a suggestion on how to overcome this issue that will be a great help

8
  • Hmm, can you explain why you need to be using a generic D at all? What's the difference between what you're doing here and this code which just accepts a param of type T? If this turns out to be a bug or limitation in the TS compiler, the report probably needs to show a use case that motivates it, and right now there doesn't seem to be a reason for x or y to be generic methods Commented Apr 26, 2021 at 18:49
  • Anyway, it looks this might be a minimal reproducible example of the situation. I'll see if I can find an existing GitHub issue about this. Commented Apr 26, 2021 at 19:01
  • I am using this code in the Svelte framework, and using a generic D is needed to properly inference types, without the D generic I get any type instead, Commented Apr 26, 2021 at 19:20
  • I'm more interested in a self-contained use case. A function type like <T extends X>(x: T)=>void has no reason to be generic, since it can be replaced with (x: X)=>void. On the other hand, a function type like <T extends X>(x: T) => T or <T extends X>(x: T, cb: (x: T)=>void)=>void or something that mentions T at least two times cannot be replaced with a non-generic version without losing precision. If I raise a GitHub issue I'll probably just do something like this Commented Apr 26, 2021 at 19:33
  • It's quite possible the answer here will be that type inference won't work for you and you'll have to manually specify the types more than you'd like. Commented Apr 26, 2021 at 19:35

1 Answer 1

1

This is a bug in the TypeScript compiler, which should (thanks to this question) be fixed for the next TypeScript release.


The error you are seeing is surprising. On the face of it, the function type <D extends { a: number; b: number; }>(d: D, g: number) => number should indeed be assignable to <D extends { a: number; b: number; }>(d: D, g: number) => string | number | Date. The compiler is apparently under the impression that the D type parameter from the inferred type of the function you are supplying might not be assignable to the D type parameter required by the Component constructor... but that doesn't make much sense because D in both cases is a parameter and not a specific type. After some research I could not find an existing related issue in TypeScript's GitHub repository, so, I filed microsoft/TypeScript#43833 to ask what's going on.

And, as I mentioned, apparently it was considered a bug in the TypeScript compiler. Additionally, it has been fixed; see microsoft/TypeScript#43835. So the good news is that it should be fixed in the next release of TypeScript.


Until a fixed version is available for you to use, the only workaround I've found that keeps your function generic is to manually annotate the types that are currently being inferred contextually. This is redundant, but at least it works. If you don't want to write out the type {a: number, b: number}, you could do something like this:

const t = { a: 10, b: 20 };
const comp = new Component({
  T: t,
  x: <D extends typeof t>(d: D, y: number) => d.a // no error now
});
/* const comp: Component<{
    a: number;
    b: number;
}, [y: number], any[]> */

Here we're letting the compiler infer the type of t as {a: number, b: number} and then in the annotation for x we use the typeof type query operator on t.

Hopefully that will tide you over until the fix comes through. 🤞

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.