
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 미만의 값만 남김