
View on GitHub: https://tsch.js.org/459
주어진 배열을 플랫한 배열 타입으로 바꾸는 Flatten 타입을 구현하세요.
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
T에 extends any[]를 활용해, 배열이 아닌 경우 에러 처리[infer First, ...infer Rest]로 배열의 각 요소 확인First가 배열인 경우, [...Flatten<First>, ...Flatten<Rest>]로 재귀 호출First가 배열이 아닌 경우, [First, ...Flatten<Rest>]로 재귀 호출1차 시도
type Flatten<T> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
View on GitHub: https://tsch.js.org/527
주어진 인터페이스에 새로운 필드를 추가한 object 타입을 구현하세요. 이 타입은 세 개의 인자를 받습니다.
type AppendToObject<
T extends object,
U extends string,
V,
MergedObject = T & Record<U, V>,
> = { [P in keyof MergedObject]: MergedObject[P] };
MergedObject는 T와 Record<U, V>를 유니온 타입으로 합친 타입[P in keyof MergedObject]는 MergedObject의 모든 키를 순회, MergedObject[P]는 MergedObject의 각 키에 해당하는 값을 반환1차 시도
type AppendToObject<T extends object, U, V> = { [P in keyof T]: T[P] } & {
U: V;
};
type ex1 = AppendToObject<test1, "home", boolean>;
// { key: "cat"; value: "green"; } & { U: boolean; }
U가 주어진 타입이 아닌, string U가 key 값이 됨2차 시도
type AppendToObject<T extends object, U extends string, V> = {
[P in keyof T]: T[P];
} & Record<U, V>;
type ex1 = AppendToObject<test1, "home", boolean>;
// { key: "cat"; value: "green"; } & Record<"home", boolean>
Record를 활용해 U를 key로 하는 객체를 생성View on GitHub: https://tsch.js.org/529
number, string, 혹은 bigint을 받는 Absolute 타입을 만드세요.
출력은 양수 문자열이어야 합니다.
type StringFormat<T extends number | string | bigint> = `${T}`;
type Absolute<T extends number | string | bigint> =
StringFormat<T> extends `${infer First}${infer Rest}`
? First extends "-"
? Rest
: `${First}${Rest}`
: never;
StringFormat<T>는 T를 문자열로 변환하는 타입StringFormat<T>으로 변환된 문자열을 파싱하여, 첫 번째 문자가 -인 경우 뒤에 있는 문자열을 반환-가 아닌 경우, 그대로 반환Typescript에서 bigint는 어떻게 표현하는가?
let foo: bigint = BigInt(100); // the BigInt function
let bar: bigint = 100n; // a BigInt literal
BigInt 함수를 통해 생성된 bigint는 number의 서브타입이 아님number는 bigint의 서브타입이 아님type IsBigInt<T> = T extends bigint ? true : false;
type Test1 = IsBigInt<bigint>; // true
type Test2 = IsBigInt<number>; // false
type IsNumber<T> = T extends number ? true : false;
type Test3 = IsNumber<number>; // true
type Test4 = IsNumber<bigint>; // false
_는 숫자 구분자임(단순히 가독성을 높이기 위함이며, bigint와 상관 없음)View on GitHub: https://tsch.js.org/531
문자열 인수를 입력받는 String to Union 유형을 구현하세요.
출력은 입력 문자열의 Union type이어야 합니다.
type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
? [First, ...StringToTuple<Last>]
: [];
type StringToUnion<T extends string> = StringToTuple<T>[number];
StringToTuple<S extends String>은 문자열을 튜플로 변환하는 타입(298-length-of-string 참고)[number]를 통해 유니온 타입으로 변환(10-tuple-to-union 참고)View on GitHub: https://tsch.js.org/599
두 개의 객체를 병합하는 제네릭 Merge<F, S>를 구현하세요.
type Merge<F, S> = {
[P in keyof F | keyof S]: P extends keyof S
? S[P]
: P extends keyof F
? F[P]
: never;
};
[P in keyof F | keyof S]를 활용해 두 객체의 키를 모두 순회P가 keyof S에 속한다면, S[P]를 먼저 반환(덮어쓰기)P가 keyof S에 속하지 않고, keyof F에 속한다면, F[P]를 반환1차 시도
type Merge<F, S, MergedObject = F & S> = {
[P in keyof MergedObject]: MergedObject[P];
};
type example = Merge<Foo, Bar>;
// { a: number; b: never; c: boolean; }
MergedObject라는 F와 S의 유니온 타입을 선언해주고, 이를 순환하는 방식왜 중복되는 키의 경우, value가 never로 처리되는지?
{ a: number } & { a: string }의 경우, { a: number & string }로 처리됨a의 타입은 number & string이 되며, 이는 유효하지 않은 타입이므로 never로 처리됨View on GitHub: https://tsch.js.org/612
camelCase나 PascalCase를 kebab-case 문자열로 수정하세요.FooBarBaz -> foo-bar-bazkebab-case로 변환하는 문제-를 붙임-를 붙이지 않음type IsAlphabet<T extends string> =
Lowercase<T> extends Uppercase<T> ? false : true;
type IsUpperCase<T extends string> =
IsAlphabet<T> extends true ? (Uppercase<T> extends T ? true : false) : false;
type MakeKebabCase<S extends string> = S extends `${infer First}${infer Rest}`
? IsUpperCase<First> extends true
? `-${Lowercase<First>}${MakeKebabCase<Rest>}`
: `${First}${MakeKebabCase<Rest>}`
: "";
type KebabCase<S extends string> = S extends `${infer First}${infer Rest}`
? IsUpperCase<First> extends true
? `${Lowercase<First>}${MakeKebabCase<Rest>}`
: `${First}${MakeKebabCase<Rest>}`
: "";
IsAlphabet 타입은 문자열이 알파벳인지 확인하는 타입(만약 T가 알파벳이라면 Lowercase<T>가 Uppercase<T>의 서브타입이 될 수 없음)IsUpperCase 타입은 문자열이 알파벳일 경우, 대문자인지 확인하는 타입MakeKebabCase 타입은 First가 대문자일 경우, Lowercase<First>를 통해 소문자로 변환한 뒤 -를 붙이고 Rest를 재귀적으로 호출KebabCase 타입은 최초의 First가 대문자일 경우, Lowercase<First>를 통해 소문자로 변환한 뒤 -를 붙이지 않고, Rest를 MakeKebabCase 타입으로 호출1차 시도
type IsUpperCase<T extends string> = T extends Uppercase<T> ? true : false;
type MakeKebabCase<S extends string> = S extends `${infer First}${infer Rest}`
? IsUpperCase<First> extends true
? `-${Lowercase<First>}${MakeKebabCase<Rest>}`
: `${First}${MakeKebabCase<Rest>}`
: "";
type KebabCase<S extends string> = S extends `${infer First}${infer Rest}`
? IsUpperCase<First> extends true
? `${Lowercase<First>}${MakeKebabCase<Rest>}`
: `${First}${MakeKebabCase<Rest>}`
: "";
type ex1 = KebabCase<"foo-bar">;
// "foo--bar"
type ex2 = KebabCase<"foo_bar">;
// "foo-_bar"
type ex3 = KebabCase<"Foo-Bar">;
// "foo---bar"
IsUpperCase가 무조건 true를 반환하여 -를 붙임