타입스크립트를 사용하다보면, 더 정확한 타입 추론을 위해 함수의 파라미터를 string
, number
따위 대신 리터럴로 받고 싶어질 때가 있다.
굉장히 인공적인 예시이지만 다음 예시를 살펴보자.
타입을 리터럴로 받고 싶은 상황의 예시
function returnTypeofString(name: string) {
return name;
}
// both types are string 🤯
const res = returnTypeofString("abc");
const res2 = returnTypeofString('John' as const);
나는 아주 강력한 타입 추론을 위해 res의 타입이 'abc'
와 같이 나오길 원한다.
하지만 타입은 string
으로 나오게 된다.
하지만 이런 상황에서 리터럴로 타입을 받고 싶을 경우 어떻게 할까?
방법
type StringLiteral<T> = T extends string ? string extends T ? never : T : never;
이런 기가 막힌 방법이 있다 💀
와우! 이것으로 타입 추론을 극한까지 올릴 수 있게 되었다.
다른 타입의 리터럴들
type StringLiteral<T> = T extends string ? string extends T ? never : T : never;
type NumberLiteral<T> = T extends number ? number extends T ? never : T : never;
type BooleanLiteral<T> = T extends boolean ? boolean extends T ? never : T : never;
꼭 string이 아니더라도, number, boolean도 리터럴로 받을 수 있다.
type Literal<T> = StringLiteral<T> | NumberLiteral<T> | BooleanLiteral<T>;
이런 식으로 활용할 수도 있겠다?
응용
사실 이런 이상한 결벽같은 타입추론을 이용할만한 곳은 빌더패턴이나 TypeORM 같이 문자열로 타입을 가져와야하는 곳이 아닐까 한다.
빌더 패턴
type StringLiteral<T> = T extends string ? string extends T ? never : T : never;
type NumberLiteral<T> = T extends number ? number extends T ? never : T : never;
type BooleanLiteral<T> = T extends boolean ? boolean extends T ? never : T : never;
type Literal<T> = StringLiteral<T> | NumberLiteral<T> | BooleanLiteral<T>;
class Builder<T extends ReadonlyArray<unknown>> {
private constructor(private readonly value: T) {}
public static create() {
return new Builder([] as const);
}
public add<K>(value: Literal<K>): Builder<[...T, K]> {
return new Builder([...this.value, value]);
}
public build(): T {
return this.value;
}
}
// 실사용
const result = Builder
.create()
.add(1)
.add("a")
.add(true)
.build();
이런 식으로다가 빌더 패턴을 만들어볼 수 있겠다.
문자열로 타입을 가져와야 할 때
class User {
constructor(
public id: number,
public name: string,
public age: number,
public isMale: boolean
) {}
}
class UserRepository {
public select<T extends keyof User>(findQuery: {
column: StringLiteral<T>[];
where: Partial<User>;
}): Pick<User, T> {
return {} as any;
}
}
이런 기가 막히게 TypeORM스러운 클래스를 만들어보자.
오! 이렇게 TypeORM은 이루어내지 못했던 제대로 된 타입 추론을 해낼 수 있었다.
'언어 > Javascript & Typescript' 카테고리의 다른 글
우리 회사 웹 서비스 좌충우돌 Yarn Berry 도입기 (0) | 2023.08.22 |
---|---|
레거시 프론트엔드 프로젝트 점진적으로 개선하기 (0) | 2023.04.13 |
CDN에서 받아온 js에 타입 넣기 (0) | 2023.04.13 |
배열 안의 원소들을 파라미터로 하는 Promise 체이닝하기 (0) | 2023.04.13 |
자바스크립트로 난수 만들기 (0) | 2022.09.06 |