Mahi
Mahi

Reputation: 21932

TypeScript generic's type inference fails if generic is used as a callback's argument

This compiles just fine:

class Foo<F> {
  bar: Array<F> = [];

  static create<F, T extends Foo<F>>(this: new () => T): T {
    return new this();
  }
}

class Foo2 extends Foo<string> {}

const foo = Foo2.create();

But if I convert Array<F> to Array<(a: F) => void>, the same code fails:

class Foo<F> {
  bar: Array<(a: F) => void> = [];

  static create<F, T extends Foo<F>>(this: new () => T): T {
    return new this();
  }
}

class Foo2 extends Foo<string> {}

const foo = Foo2.create();
      ^^^
The 'this' context of type 'typeof Foo2' is not assignable to method's 'this' of type 'new () => Foo<unknown>'.
  Types of construct signatures are incompatible.
    Type 'new () => Foo2' is not assignable to type 'new () => Foo<unknown>'.
      Type 'Foo2' is not assignable to type 'Foo<unknown>'.
        Type 'unknown' is not assignable to type 'string'. ts(2684)

It works if I type it manually:

const foo = Foo2.create<string, Foo2>();

So for some reason TypeScript can't infer the string type if it's used in a function argument. Why is this?

Upvotes: 3

Views: 137

Answers (1)

Meligy
Meligy

Reputation: 36594

I'm not sure why TypeScript is not figuring the type out, but I think you had a few mistakes that you got away with anyway, particularly the F type in the method declaration overriding the F in the class itself.

Your code can be simplified as follows:

class Foo<F> {
  bar: Array<(a: F) => void> = [];

  static create<T>(this: new () => T) {
    return new this();
  }
}

class Foo2 extends Foo<string> {}


const foo = Foo2.create();

And it'll work fine:

Playground Link

Update: Sorry, I misread the question originally. The answer has since been updated.

Upvotes: 1

Related Questions