안녕하세요?
오늘은 좀 어려운 주제인데요.
먼저, RPG 게임에서 player의 특성을 정의하는 타입을 만들려고 한다고 가정해 봅시다.
어떻게 해야 할까요?
먼저, name이 있어야겠죠?
type MyFlexibleInfo = {
name: string;
}
그다음으로 race (종족)이나 age (나이)가 있어야겠죠?
만약, MyFlexibleInfo 가 두 가지의 타입만 있다고 하고 race나 age 중에 한 개만 자유롭게 넣게 하고 싶을 때는 어떻게 할까요?
바로 타입 머지 (type merge) 하면 되는데요.
type MyFlexibleInfo = {
name: string;
} & Record<string, string>
Record 유틸리티 타입으로 key : value 타입을 추가할 수 있게 했습니다.
type MyFlexibleInfo = {
name: string;
} & Record<string, string>
const player1: MyFlexibleInfo = {
name: "TS",
race: "human"
}
console.log(player1);
위 코드처럼 race 부분을 넣을 수 있고,
type MyFlexibleInfo = {
name: string;
} & Record<string, string>
const player1: MyFlexibleInfo = {
name: "TS",
age: "14"
}
console.log(player1);
위 코드처럼 age 부분을 string화 해서 넣을 수 도 있습니다.
그러면 race나 age 부분을 자유롭게 추가하려면 어떻게 해야 할까요?
바로 다음과 같이 하시면 됩니다.
type MyFlexibleInfo = {
name: string;
[key: string] : string | number;
}
key value 부분을 위와 같이 지정하면 key 부분은 string이 되고 value 부분은 string이나 number가 됩니다.
type MyFlexibleInfo = {
name: string;
[key: string]: string | number;
}
const player1: MyFlexibleInfo = {
name: "TS",
race: "human",
age: 14,
}
console.log(player1);
어떤가요?
타입 정의에 있어 매우 유용하고 유연한 타입이 되지 않았나요?
Re-Mapped Type
타입을 리맵(바꿀)할 수 있는 Type을 알아볼까 하는데요.
타입 스크립트 다큐먼트에서 예제를 가져와서 적용해 보겠습니다.
https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
type OptionsFlags<T> = {
[Property in keyof T]: boolean;
};
Property를 boolean으로 바꾸는 플래그인데요.
Property는 T 타입의 keyof 연산자를 이용해서 가져왔습니다.
이제 실제 코드에서 적용해 볼까요?
interface PlayerInfo {
name: string;
age: number;
}
PlayerInfo 가 위와 같이 interface로 있다고 합시다.
name은 string이고, age는 number인데요.
name과 age 타입을 바꿔볼까 합니다. Re-Mapping이라고 하는데요.
interface PlayerInfo {
name: string;
age: number;
}
type OptionsFlags<T> = {
[Property in keyof T]: boolean;
}
type PlayerInfoOption = OptionsFlags<PlayerInfo>;
이제 PlayerInfoOption의 타입을 살펴볼까요?
위 스크린숏을 보시면 PlayerInfoOption의 name과 age 타입이 boolean으로 바뀌었습니다.
신기하네요.
이번에는 string으로 바꿔 볼까요?
코딩할 때 이게 필요할 때가 있을 때가 언젠가는 한번 나오겠죠.
좀 더 나아가서 콜백 함수 타입도 바꿔 볼까요?
function listenToObject(obj, listeners): void {
console.log("Wait");
}
const player2: PlayerInfo = {
name: "GOOD",
age: 14
}
listenToObject(player2, {
onNameChange: (v: string) => { },
onAgeChange: (v: number) => { },
})
listenToObject 함수가 있고, listeners는 onNameChange과 onAgeChange 함수가 있다고 합시다.
이제 제네릭을 적용해 볼까요?
type Listeners<T> = {
[Property in keyof T]: () => void;
}
function listenToObject<T>(obj: T, listeners: Listeners<T>): void {
console.log("Wait");
}
const player2: PlayerInfo = {
name: "GOOD",
age: 14
}
Listeners <T> 도 만들었습니다.
마지막으로 PlayerInfoListeners 도 타입을 아래와 같이 지정해 줬습니다.
type PlayerInfoListeners = Listeners<PlayerInfo>;
이 PlayerInfoListeners 타입을 볼까요?
어떤가요? 콜백 함수의 타입도 () => void로 바꿨습니다.
타입 Listeners <T>에서 리턴되는 콜백 함수의 인자를 아래와 같이 바꿔 볼까요?
type Listeners<T> = {
[Property in keyof T]: (newValue: T[Property]) => void;
}
이렇게 되면 PlayerInfoListeners는 어떤 타입이 되어 있을까요?
와우, 콜백 함수의 인수를 바꿔 버렸네요.
void도 다르게 바꿔볼까요?
type Listeners<T> = {
[Property in keyof T]: (newValue: T[Property]) => string;
}
어떤가요?
Re-Mapped Type 방법인데요.
외울 거는 [Property in keyof T]를 꼭 기억하시길 바랍니다.
'코딩 > Typescript' 카테고리의 다른 글
Next.js로 충분할까? 왜 일부 개발자들은 NestJS를 선택할까? (0) | 2024.09.02 |
---|---|
타입스크립트 클래스의 readonly와 static 알아보기 (0) | 2022.03.01 |
타입스크립트 Generics in Class - 타입스크립트 TypeScript 강좌 13편 (0) | 2022.03.01 |
타입스크립트 class visibility - 타입스크립트 TypeScript 강좌 12편 (0) | 2021.12.12 |
타입스크립트 enum, Literal types - 타입스크립트 TypeScript 강좌 11편 (1) | 2021.12.12 |