View on GitHub: https://tsch.js.org/5310
Implement the type version of Array.join, Join<T, U> takes an Array T, string or number U and returns the Array T with U stitching up.
type Res = Join<["a", "p", "p", "l", "e"], "-">; // expected to be 'a-p-p-l-e'
type Res1 = Join<["Hello", "World"], " ">; // expected to be 'Hello World'
type Res2 = Join<["2", "2", "2"], 1>; // expected to be '21212'
type Res3 = Join<["o"], "u">; // expected to be 'o'
Array.join()
메서드를 타입 레벨에서 구현T
와 문자열 또는 숫자 U
를 받아서 배열 T
의 요소를 U
로 구분하여 문자열로 반환T
의 요소 타입은 문자열 또는 숫자U
는 문자열 또는 숫자접근 방법
코드
type Join<
T extends (string | number)[],
U extends string | number
> = T extends [
infer First extends string | number,
...infer Rest extends (string | number)[]
]
? T["length"] extends 1
? `${First}`
: `${First}${U}${Join<Rest, U>}`
: "";
실패 원인
접근 방법
U
가 존재하지 않으면 기본값으로 ,
추가코드
type Join<
T extends (string | number)[],
U extends string | number = ","
> = T extends [
infer First extends string | number,
...infer Rest extends (string | number)[]
]
? T["length"] extends 1
? `${First}`
: `${First}${U}${Join<Rest, U>}`
: "";
코드 설명
T
를 infer로 분리하여 첫번째 요소와 나머지 요소로 분리T["length"]
가 1이면 마지막 요소이기 때문에, 그냥 반환U
를 묶어서 재귀 호출View on GitHub: https://tsch.js.org/5317
Implement the type version of Array.lastIndexOf
, LastIndexOf<T, U>
takes an Array T
, any U
and returns the index of the last U
in Array T
type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2>; // 3
type Res2 = LastIndexOf<[0, 0, 0], 2>; // -1
Array.lastIndexOf()
메서드를 타입 레벨에서 구현T
와 값 U
를 받아서, 배열의 마지막 U
의 인덱스를 반환T
는 배열U
는 배열의 요소 타입접근 방식
indexof
로직을 활용result
에 저장result
반환코드
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
type LastIndexOf<
T extends any[],
U,
IndexArr extends unknown[] = [],
Result = -1
> = T extends [infer First, ...infer Rest]
? MyEqual<First, U> extends true
? LastIndexOf<Rest, U, [...IndexArr, unknown], IndexArr["length"]>
: LastIndexOf<Rest, U, [...IndexArr, unknown], Result>
: Result;
코드 설명
T
를 infer로 분리하여 첫번째 요소와 나머지 요소로 분리MyEqual
을 통해 첫번째 요소와 U
를 비교IndexArr
의 길이를 Result
에 저장IndexArr
에 unknown
추가(IndexArr
의 길이가 곧 해당 요소의 index
)Result
반환View on GitHub: https://tsch.js.org/5360
Implement the type version of Lodash.uniq
, Unique<T>
takes an Array T
, returns the Array T
without repeated values.
type Res = Unique<[1, 1, 2, 2, 3, 3]>; // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]>; // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, "a", 2, "b", 2, "a"]>; // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, "a", 1, string, 2, "b", 2, number]>; // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]>; // expected to be [unknown, any, never]
Lodash.uniq
메서드를 타입 레벨에서 구현T
를 받아서, 배열의 중복된 값을 제거한 배열을 반환코드
type Unique<
T extends any[],
Exists = never,
Result extends any[] = []
> = T extends [infer First, ...infer Rest]
? First extends Exists
? Unique<Rest, Exists, Result>
: Unique<Rest, Exists | First, [...Result, First]>
: Result;
실패 이유
any
등의 타입은 extends
로 비교할 수 없음.MyEqual
타입을 만들어서 비교해야 함.접근 방식
MyEqual
타입을 만들고, 유니온 각 요소를 확인하면서 MyEqual
이 단 하나라도 true
인지 확인코드
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
type MyExtends<T, Exists extends any[]> = Exists extends [
infer First,
...infer Rest
]
? MyEqual<T, First> extends true
? true
: MyExtends<T, Rest>
: false;
type Unique<
T extends any[],
Exists extends any[] = [],
Result extends any[] = []
> = T extends [infer First, ...infer Rest]
? MyExtends<First, Exists> extends true
? Unique<Rest, Exists, Result>
: Unique<Rest, [...Exists, First], [...Result, First]>
: Result;
코드 설명
MyEqual
타입을 만들고, 유니온 각 요소를 확인하면서 MyEqual
이 단 하나라도 true
인지 확인true
라면 무시하고 나머지 재귀호출false
라면 해당 요소를 유니온에 추가하고 나머지 재귀호출Result
반환View on GitHub: https://tsch.js.org/5821
Implement MapTypes<T, R>
which will transform types in object T
to different types defined by type R
which has the following structure
type StringToNumber = {
mapFrom: string; // value of key which value is string
mapTo: number; // will be transformed for number
};
Examples:
type StringToNumber = { mapFrom: string; mapTo: number };
MapTypes<{ iWillBeANumberOneDay: string }, StringToNumber>; // gives { iWillBeANumberOneDay: number; }
// Be aware that user can provide a union of types:
type StringToNumber = { mapFrom: string; mapTo: number };
type StringToDate = { mapFrom: string; mapTo: Date };
MapTypes<{ iWillBeNumberOrDate: string }, StringToDate | StringToNumber>; // gives { iWillBeNumberOrDate: number | Date; }
// If the type doesn't exist in our map, leave it as it was:
type StringToNumber = { mapFrom: string; mapTo: number };
MapTypes<
{ iWillBeANumberOneDay: string; iWillStayTheSame: Function },
StringToNumber
>; // // gives { iWillBeANumberOneDay: number, iWillStayTheSame: Function }
MapTypes<T, R>
타입은 객체 T
의 타입을 변환하는 타입R
은 변환 규칙을 정의하는 타입R
의 구조는 다음과 같음 (mapFrom
과 mapTo
가 있음)R
은 여러 개의 타입을 유니온으로 받을 수 있음R
에 없는 타입은 그대로 반환T
를 순회하면서, T[K]
가 R
의 mapFrom
과 같은 타입인지 확인mapTo
타입으로 변환코드
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
// mapFrom과 mapTo를 어떻게 꺼내오지?
// type MapTypes<T, R> = {
// [K in keyof T]: R extends R
// ? MyEqual<R["mapFrom"], K> extends true
// ? R["mapTo"]
// : T[K]
// : T[K];
// };
// infer로 mapFrom과 mapTo를 꺼내옴
type MapTypes<T, R> = {
[K in keyof T]: R extends { mapFrom: infer From; mapTo: infer To }
? T[K] extends From
? To
: T[K]
: never;
};
실패 이유
type example = MapTypes<
{ name: string; date: Date },
{ mapFrom: string; mapTo: boolean } | { mapFrom: Date; mapTo: string }
>;
접근 방식
코드
type MapTypes<T, R extends { mapFrom: any; mapTo: any }> = {
[K in keyof T]: T[K] extends R["mapFrom"]
? R extends { mapFrom: T[K] }
? R["mapTo"]
: never
: T[K];
};
코드 설명
R
을 { mapFrom: any; mapTo: any }
로 제한T[K]
와 R["mapFrom"]
를 비교extends
가 가능하다면, R
중 { mapFrom: T[K] }
에 extends
가능한 요소만 남김(만약 extends
가 불가능하다면, never
반환)mapTo
타입으로 변환extends
가 불가능하다면, T[K]
그대로 반환View on GitHub: https://tsch.js.org/7544
Construct a tuple with a given length.
type result = ConstructTuple<2>; // expect to be [unknown, unkonwn]
unknown
타입의 요소를 가진 튜플을 반환접근 방식
Result
의 length
가 L
이 될 때까지 재귀 호출코드
type ConstructTuple<
L extends number,
Result extends unknown[] = []
> = Result["length"] extends L
? Result
: ConstructTuple<L, [...Result, unknown]>;
코드 설명
Result
의 length
가 L
이 될 때까지 재귀 호출Result
의 length
가 L
이 되면 튜플을 반환의문점
View on GitHub: https://tsch.js.org/8640
Sometimes we want to limit the range of numbers...
type result = NumberRange<2, 9>; // | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
접근 방식
코드
type ConstructTuple<
L extends number,
Result extends unknown[] = []
> = Result["length"] extends L
? Result
: ConstructTuple<L, [...Result, unknown]>;
type NumberRange<
L extends number,
H extends number,
Arr extends unknown[] = ConstructTuple<L>
> = Arr["length"] extends H
? Arr["length"]
: Arr["length"] | NumberRange<L, H, [...Arr, unknown]>;
실패 이유
접근 방식
ConstructTuple
을 사용하지 않고, 재귀를 통해 범위의 숫자를 반환코드
type NumberRange<
L extends number,
H extends number,
Arr extends unknown[] = [],
IsCount = false
> = Arr["length"] extends H
? H
: IsCount extends true
? Arr["length"] | NumberRange<L, H, [...Arr, unknown], IsCount>
: Arr["length"] extends L
? L | NumberRange<L, H, [...Arr, unknown], true>
: NumberRange<L, H, [...Arr, unknown], false>;
실패 이유
접근 방식
코드
type Utils<L, C extends any[] = [], R = L> = C["length"] extends L
? R
: Utils<L, [...C, 0], C["length"] | R>;
type NumberRange<L, H> = L | Exclude<Utils<H>, Utils<L>>;
type example = NumberRange<2, 9>;
코드 설명
Utils
는 재귀를 통해 L 길이 만큼의 배열을 만듦C["length"] extends L
이라면, C
의 길이가 L
에 도달R
에는 0
부터 L
까지의 값이 유니온으로 관리되고 있음Exclude
를 통해 L
이상 H
미만의 값만 남김