Reputation: 43
The function createBob has a TS error because Person is an abstract class and I can't instantiate it. I want the function to only accept the classes that extend Person and are not abstract.
abstract class Person {
protected abstract type?: string
constructor(private name: string) {}
protected greet() {
console.log(`Hello ${this.name}`)
}
}
class Student extends Person {
type = "Student"
constructor(type: string) {
super(type)
}
}
class Teacher extends Person {
type = "teacher"
constructor(type: string) {
super(type)
}
}
function createPerson(ClassThatInheritsPerson: typeof Person): Person {
/*
* The ClassThatInheritsPerson should only accept classes that extend Person but are not Person
*/
return new ClassThatInheritsPerson()
}
const teacher1 = createPerson(Teacher) // should be allowed
const student1 = createPerson(Student) // should be allowed
const person = createPerson(Person) // shouldn't be allowed
Upvotes: 1
Views: 94
Reputation: 20494
You can't pass types and use them to instantiate classes like you can do in other languages. What you need to do is change the type of createPerson's argument to a constructor by using new() => Person
. This will allow you create new instances of Person, while throwing a compiler error for the abstract case:
function createPerson<T extends Person>(ctor: new() => T): T {
return new ctor();
}
const student1 = createPerson(Student); // should be allowed
const teacher1 = createPerson(Teacher); // should be allowed
const foo = createPerson(Foo); // typeof Foo isn't asignable to () => Person
const person1 = createPerson(Person) // Error: Cannot assign an abstract
A few other suggestions. Don't make type protected - that will be useful for discriminating types later. You could also either make the type of type a string literal, or use a generic parameter to specify the type. That way something like student.type
won't be type string, but type 'student'.
abstract class Person<T extends string> {
constructor(public readonly type: T) { }
}
class Student extends Person<'Student'> {
constructor() {
super('Student');
}
}
class Teacher extends Person<'teacher'> {
constructor() {
super('teacher');
}
}
Upvotes: 1