0
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?

2
  • @pilchard The answer is hidden in there but it's very hard to understand it if you don't already know it. This question is using typeof Class to get a constructor which the question you mentioned is doesn't mention. Using typeof Class instead of a generic constructor type is what caused the problem Commented Dec 17, 2021 at 0:59
  • To those who closed this question. The accepted answer on the linked question is to use InstanceType<T> which the OP was already using. The linked answer does not answer the problem posted here which was how to create a generic constructor type. The solution to the OP's problem does not involve using InstanceType. Please don't close a question linking to another question whose accepted answer does not address this post's problem. Commented Dec 17, 2021 at 15:59

1 Answer 1

0

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

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

3 Comments

Did you mean const f = <C extends Class> (c: ClassCtor<C>): C => {...? (you never use your constructor type). Nice explanation though.
I just inlined the constructor type... But yes, that's how it would be used.
I saw that, just thought it might clarify the concept to make use of the custom type.

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.