0

I'd like to create a function which could only accept generic types which were subclass of a centain base class and reject all the other types such as:

class A {}
class B extends A {}
class C {}

function foo<T extends A> () : T {
  return undefined
}

foo<B>()
foo<C>()   // <- I'd like the compiler throw out an error on this line 

How can I make the foo<C>() fail in compilation?

2
  • You can't return undefined in foo function! Commented Jun 8, 2021 at 3:43
  • @AlirezaAhmadi Don't mind that's a example code. Commented Jun 8, 2021 at 3:55

1 Answer 1

2

Would you believe me if I told you C actually is a subtype of A? Typescript is structurally typed, so any object that implements a given interface for A is considered a valid value of type A. Since A's interface is {} (i.e. no interface at all), any Javascript object is an value of type A.

If the interfaces don't match, then the subclass check will fail.

class A {foo() {}}
class B extends A {}
class C {}

Now C is not a subtype of A. If you want to be absolutely sure, you can make a private field. Private fields are never compared structurally, so a private field on A can only ever be present in actual subclasses of A.

class A {private _placeholder: null = null}
class B extends A {}
class C {}

There's a distinction between subclassing and subtyping here. In some languages like Java, the two are similar enough that you can treat them as interchangeable in many situations, but not so in Typescript. In your first code example, B is a subclass of A (because it subclasses it with the extends keyword, or moreover because A appears in the prototype chain for instances of B). However, C and B are both subtypes of A (since instances of either can safely be substituted anywhere a value of type A is expected). In general, every subclass relationship defines a subtype relationship, but not every subtype relationship is defined by a subclass relationship.

This is also true of union and intersection types. It's completely accurate to say that string is a subtype of number | string, since a string can always be safely treated as a number | string, but it's not accurate to say that string is a subclass of number | string (not least of all since neither of the things mentioned is a class).

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

1 Comment

Does that mean the generic subsitution is based on subtype instead of subclass?

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.