In Typescript how to get the generic return type of a method on a class instantiated in a factory(ish) function

Viewed 352

In the example code below, TypeScript can't infer the generic type U in the fooBar function and as a result the return type of fooRef.doIt() is unknown. Why is that and what do I need to modify in order to get the proper return type?

interface Foo<T> {
  foo(): T;
}

class Bar implements Foo<string> {
  foo(): string {
    return 'Bar';
  }
}

class Baz implements Foo<number> {
  foo(): number {
    return 43;
  }
}

class FooRef<T extends Foo<U>, U> {
  constructor(private instance: T) {}

  doIt() {
    return this.instance.foo();
  }
}

function fooBar<T extends Foo<U>, U>(foo: new (...args: any[]) => T) {
  return new FooRef(new foo());
}

const barRef = fooBar(Bar);
barRef.doIt(); // unknown, expected string

const bazRef = fooBar(Baz);
bazRef.doIt(); // unknown, expected number
1 Answers

This answer was inspired by @jcalz's suggestion in the comments. I wasn't able to make it work using infer, as he suggested, but his suggestion using ReturnType works as intended and this is what I use here.

interface Foo<T> {
    foo(): T;
}

class Bar implements Foo<string> {
    foo(): string {
        return 'Bar';
    }
}

class Baz implements Foo<number> {
    foo(): number {
        return 43;
    }
}

class FooRef<T extends Foo<U>, U = ReturnType<T["foo"]>> {
    constructor(public instance: T) { }

    doIt(): U {
        return this.instance.foo();
    }
}

function fooBar<T extends Foo<any>>(foo: new (...args: any[]) => T) {
    return new FooRef(new foo());
}

const barRef = fooBar(Bar);
barRef.instance // (property) FooRef<Bar, string>.instance: Bar
console.log(barRef.doIt().toUpperCase()); // "BAR"

const bazRef = fooBar(Baz);
bazRef.instance // (property) FooRef<Baz, number>.instance: Baz 
console.log(bazRef.doIt().toFixed(2)) // "43.00"
Related