View on GitHub: https://tsch.js.org/110
문자열의 첫 글자만 대문자로 바꾸고 나머지는 그대로 놔두는 Capitalize<T>
를 구현하세요.
type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Rest}`
: "";
// 1차 시도
type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
? `${First["toUpperCase"]}${Rest}`
: "";
infer
키워드를 사용하여 문자열을 첫 글자와 나머지 문자열로 분리Uppercase
타입으로 변환 (S["toUpperCase"]
가 될 줄 알았음...)View on GitHub: https://tsch.js.org/116
문자열 S에서 From
를 찾아 한 번만 To
로 교체하는 Replace<S, From, To>
를 구현하세요.
type Replace<
S extends string,
From extends string,
To extends string,
> = From extends ""
? S
: S extends `${infer RestHead}${From}${infer RestTail}`
? `${RestHead}${To}${RestTail}`
: S;
// 1차 시도
type Replace<
S extends string,
From extends string,
To extends string,
> = S extends `${infer RestHead}${From}${infer RestTail}`
? `${RestHead}${To}${RestTail}`
: S;
From
이 빈 문자열인 경우 원래 문자열을 반환infer
키워드를 사용하여 문자열을 RestHead
, From
, RestTail
로 분리S
가 From
을 포함하는 경우(조건이 True 일 때) From
을 To
로 교체하고 나머지 문자열을 합쳐서 반환From
이 빈 문자열이 아니고 S
가 From
을 포함하지 않는 경우 원래 문자열을 반환View on GitHub: https://tsch.js.org/119
문자열 S에서 From
를 찾아 모두 To
로 교체하는 ReplaceAll<S, From, To>
를 구현하세요.
type ReplaceAll<
S extends string,
From extends string,
To extends string,
> = From extends ""
? S
: S extends `${infer RestHead}${From}${infer RestTail}`
? `${RestHead}${To}${ReplaceAll<RestTail, From, To>}`
: S;
// 1차 시도
type ReplaceAll<
S extends string,
From extends string,
To extends string,
> = S extends `${infer RestHead}${From}${infer RestTail}`
? ReplaceAll<`${RestHead}${To}${RestTail}`, From, To>
: S;
From
이 빈 문자열인 경우 원래 문자열을 반환infer
키워드를 사용하여 문자열을 RestHead
, From
, RestTail
로 분리S
가 From
을 포함하는 경우(조건이 True 일 때) From
을 To
로 교체하고 From
이후 문자열(RestTail
)을 재귀적으로 처리From
이 빈 문자열이 아니고 S
가 From
을 포함하지 않는 경우 원래 문자열을 반환View on GitHub: https://tsch.js.org/191
함수 타입 Fn
과 어떤 타입 A
가 주어질 때 Fn
의 인수와 A
를 마지막 인수로 받는 Fn
과 동일한 함수 유형인 G
를 생성하세요.
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (
...args: infer Args
) => infer Return
? (...args: [...Args, A]) => Return
: never;
// 1차 시도
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (
...args: infer Args
) => infer Return
? ([Args, A]) => Return
: never;
Fn
에서 인수와 반환 타입을 추출해야 됨[easy] 3312-parameters
문제를 참고하여 인수를 추출[medium] 2-return-type
문제를 참고하여 반환 타입을 추출View on GitHub: https://tsch.js.org/296
주어진 유니언 타입을 순열 배열로 바꾸는 Permutation
타입을 구현하세요.
type Permutation<T, K = T> = [T] extends [never]
? []
: K extends K
? [K, ...Permutation<Exclude<T, K>>]
: never;
<T, K = T>
: T
를 기본값으로 설정하는 변수 K
생성
K extends K
에서 유니온 분배를 위해 사용[T] extends [never]
: T
가 never
인 경우를 체크
T
가 never
인 경우, 빈 배열을 반환해서 순열 생산 종료 (재귀 종료)T extends never
를 사용하지 않는 이유는 분배법칙을 막기 위해서K extends K
: T
와 동일한 K
를 유니온 분배하여 순회
K
(유니온)를 순회하며 각 요소에 대해 재귀 호출[K, ...Permutation<Exclude<T, K>>]
: K
를 제외한 T
의 순열을 구하는 재귀 호출
K extends K
에서 분배된 요소를 순회하며 재귀 호출Exclude<T, K>
: T
에서 K
를 제외한 타입으로 재귀 호출type Test = Permutation<"A" | "B" | "C">;
// 1단계: K = 'A'
// 결과: ['A', ...Permutation<'B' | 'C'>]
// 2단계: K = 'B'
// 결과: ['B', ...Permutation<'A' | 'C'>]
// 3단계: K = 'C'
// 결과: ['C', ...Permutation<'A' | 'B'>]
K extends K
제대로 이해하기
K extends K
는 항상 참이므로, 결국 모든 유니온 분배를 트리거하는 역할type Test<K> = K extends K ? [K] : never;
type Result = Test<"A" | "B" | "C">;
'A' extends 'A' ? ['A'] : never → ['A']
'B' extends 'B' ? ['B'] : never → ['B']
'C' extends 'C' ? ['C'] : never → ['C']
그리고 해당 결과를 유니온으로 합쳐 ['A'] | ['B'] | ['C']
반환
만약 분배법칙을 적용하지 않으려면 K
를 배열로 감싸서 [K]
로 표현해야 함
type NoDistribute<K> = [K] extends [K] ? [K] : never;
type Result = NoDistribute<"A" | "B" | "C">;
// Result는 ['A' | 'B' | 'C']
View on GitHub: https://tsch.js.org/298
String#length
처럼 동작하는 문자열 리터럴의 길이를 구하세요.
type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
? [First, ...StringToTuple<Last>]
: [];
type LengthOfString<S extends string> = StringToTuple<S>["length"];
StringToTuple
함수는 문자열을 튜플로 변환하는 함수String
타입이 인자로 들어오면 한 글자씩 나눠 튜플로 변환