Home > other >  TypeScript generic function, unexpected behavior when generic is a class
TypeScript generic function, unexpected behavior when generic is a class

Time:12-18

class Class {
}
const f0 = <T extends typeof Class> (c:T): T => {
  return c
}
const call0 = f0 (Class) //ok

const f1 = <T extends typeof Class> (c:T): T => {
  const a = new c()
  return a //TS2322: Type 'Class' is not assignable to type 'T'.   'T' could be instantiated with an arbitrary type which could be unrelated to 'Class'.
}
const call1 = f1 (Class)

const f2 = <T extends typeof Class> (c:T):InstanceType<T> => {
  const a = new c()
  return a //TS2322: Type 'Class' is not assignable to type 'InstanceType '.
}
const call2 = f1 (Class)

TypeScript Playground

The argument is typed as T, so why isn't that acceptable for the return type?

CodePudding user response:

Your first snippet doesn't make a lot of sense. It says that it takes something that is a constructor that returns a Class instance. That T extends new () => Class;

It also says it returns the same T (a constructor), but you are returning an instance; that doesn't make sense.

Solution

You were a lot closer in your second snippet

const f2 = <T extends typeof Class> (c:T): InstanceType<T> => {
  const a = new c()
  return a //TS2322: Type 'Class' is not assignable to type 'InstanceType '.
}

Again, typeof Class expands to {new() => Class} but what you really want is a generic constructor, so you can't just use typeof Class, you have to write your own type for the generic constructor of Class. Then your return type is a lot simpler.

class Class {  a = 1; }

class SubClass extends Class {  b = 2; }

class OtherClass {  c = 3; }

type ClassCtor<T extends Class> = new () => T;

const f = <C extends Class> (c: new() => C): C => {
  return new c();
}

// OR
const f = <C extends Class> (c: ClassCtor<C>): C => {
  return new c();
}



// It's a Class instance
const call1 = f(Class);

// It's a SubClass instance
const call2 = f(SubClass);

// Fails compilation
const call3 = f(OtherClass);

See live example

  • Related