타입스크립트에서 클래스는 값이면서 동시에 타입으로 쓰일 수 있다.
class User {
constructor(
public name: string,
public age: number,
) {}
}
이렇게 만든 User 클래스는 user 인스턴스 객체에 대한 타입이 필요할때 사용할 수 있다.
const sayHello = (user: User) =>
`안녕 나는 ${user.name}이고 나이는 ${user.age}살 이야`
const kcs = new User('김철수', 149);
sayHello(kcs);
// 안녕 나는 김철수이고 149살 이야
sayHello({ name: '김칠수', weight: 240 });
// Argument of type '{ name: string; weight: number; }' is not assignable to parameter of type 'User'
sayHello
함수는 파라미터 user
의 타입으로 User
클래스가 선언되어있다. 파라미터 user
에는 User
클래스의 인스턴스 (또는 같은 타입의 프로퍼티들이 똑같이 들어있는 객체) 만 사용할수 있다.
그런데 어떨때는, 함수의 파라미터가 클래스의 인스턴스가 아니라, 클래스 그 자체여야 할 때도 있다.
const createInstance = (ClassToCreate: ??, args: ??) =>
new ClassToCreate(...args);
이럴때 클래스 그 자체를 묘사하는 타입
을 어떻게 만들어야 할까?
ClassType
/**
* @see https://stackoverflow.com/questions/39392853/is-there-a-type-for-class-in-typescript-and-does-any-include-it
*/
export interface ClassType<T, A extends any[] = any[]> extends Function { new(...args: A): T; }
이걸 쓰자.
stackoverflow 에 있는 여러 답변들을 조합해서 쓰기 좋게 만들었다.
class Foo {
private c: boolean
constructor(public a: string, b: number) {
this.c = !b;
}
}
/**
* 첫번째 인자에 클래스를 넣고,
* 두번째 인자에 그 클래스의 생성자가 받는 파라미터의 타입을 튜플 타입으로 넣어준다.
* 파라미터 타입이 자동으로 추론 되지 않다보니 다소 불편하긴 하다.
*/
type FooClass = ClassType<Foo, [string, number]>;
const MyFooClass: FooClass = Foo;
// MyFooClass 의 인스턴스가 Foo 와 같기때문에 타입에러가 나지 않는다.
const foo: Foo = new MyFooClass('def', 5678);
/**
* 함수의 제네릭으로 ClassType의 제네릭과 같은 제네릭을 선언해주고,
* 함수의 첫번째 파라미터로 ClassType을 선언한다.
* 생성자 파라미터 `A` 는 함수 안에서 자동으로 추론 된다.
*/
const createInstance = <T, A extends any[]>(
classToCreate: ClassType<T, A>,
constructorArgs: A,
) => {
return new classToCreate(...constructorArgs);
}
class Foo {
private c: boolean
constructor(public a: string, b: number) {
this.c = !b
}
}
// 아주 잘 작동한다.
const foo: Foo = createInstance(Foo, ['abc', 1234]);
함수 자체를 타입으로 사용해야 할 때에는 보통 함수의 파라미터로 받는 상황일 때 일 것이다. 다행히도 함수에서 우리가 만든 ClassType
을 쓸때는 생성자 파라미터에 대해 자동추론을 사용할 수 있다.