체인 가능 옵션은 일반적으로 Javascript에서 사용됩니다. 하지만 TypeScript로 전환하면 제대로 구현할 수 있나요?
이 챌린지에서는 option(key, value)과 get() 두가지 함수를 제공하는 객체(또는 클래스) 타입을 구현해야 합니다. 현재 타입을 option으로 지정된 키와 값으로 확장할 수 있고 get으로 최종 결과를 가져올 수 있어야 합니다.
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// 결과는 다음과 같습니다:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
이번 타입은 제네릭이 필요없어 보이지만 option을 통해 넣는 값을 저장할 타입 변수 하나를 생성할 것입니다. 단 기본값을 설정해줄 것입니다.
type Chainable<T extends Record<string, any> = {}> = {};
변수 T가 하나의 저장공간이 되는 것입니다 이제 option을 먼저 구현 시킵니다.
옵션은 2개의 파라미터를 받는 함수입니다.
key는 string, value는 any타입입니다. 그러나 option을 다음과 같이 구현해서는 안됩니다.
option(
key: string,
value: any
): Chainable<{ [t in keyof T]: T[t] } & { [v in string]: any }>;
이렇게 한다면 우리가 원한 값과 달리 다음과 같이 나타납니다.
const result: {
[x: string]: any;
} & {
[x: string]: any;
}
그렇기에 option 또한 2개의 타입변수를 가질 것 입니다. Key, Value 키는 string타입이고, value는 any 타입입니다.
option<Key extends string, Value extends any>
그리고 이제 이 타입을 이용하여 이전 코드와 결합한다면 다음과 같이 됩니다.
option<Key extends string, Value extends any>(
key: Key,
value: Value
): Chainable<{ [t in keyof T]: T[t] } & { [v in Key]: Value }>;
이렇게 option 함수의 타입이 완성되었습니다.
key와 value 값을 받아 이전 T와 새로운 값을 결합하여 새로운 Chainable을 제네릭을 반환하는 것 입니다.
type Chainable<T extends Record<string, any> = {}> = {
option<Key extends string, Value extends any>(
key: Key,
value: Value
): Chainable<{ [t in keyof T]: T[t] } & { [v in Key]: Value }>;
};
이제 마지막으로 get 함수를 구현할 것 입니다.
저장공간의 역할을 하고 있는 T객체를 반환 시키므로써 완성됩니다.
declare const config: Chainable;
type Chainable<T extends Record<string, any> = {}> = {
option<Key extends string, Value extends any>(
key: Key,
value: Value
): Chainable<{ [t in keyof T]: T[t] } & { [v in Key]: Value }>;
get(): T;
};
const result = config
.option("foo", 123)
.option("name", "type-challenges")
.option("bar", { value: "Hello World" })
.get();
// result
const result: {
foo: number;
name: string;
} & {
bar: {
value: string;
};
}