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-baz
kebab-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
를 반환하여 -
를 붙임