[TS] Type-Challenges 스터디 6주차
![[TS] Type-Challenges 스터디 6주차](/_next/image?url=https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Fhayou%2Fpost%2F010adb6b-bfe8-459f-ba1e-bc2419c4f47a%2Fimage.jpg&w=3840&q=75)
[Medium] 459. Flatten
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>]
: [];
- 에러 처리를 빼먹음
- 제네릭 T에 extends any[]를 활용해, 배열이 아닌 경우 에러 처리
[Medium] 527. AppendToObject
View on GitHub: https://tsch.js.org/527
문제
주어진 인터페이스에 새로운 필드를 추가한 object 타입을 구현하세요. 이 타입은 세 개의 인자를 받습니다.
문제 설명
- 인터페이스에 새로운 필드를 추가하는 문제
- 새로운 필드가 추가된 인터페이스는 온전한 타입이어야 함(유니온 타입 등 X)
정답
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가 주어진 타입이 아닌, stringU가 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로 하는 객체를 생성- 여전히 유니온 타입을 반환
[Medium] 529. Absolute
View on GitHub: https://tsch.js.org/529
문제
number, string, 혹은 bigint을 받는 Absolute 타입을 만드세요.
출력은 양수 문자열이어야 합니다.
문제 설명
- 절대값을 구해 string으로 반환하는 문제
- 음수인 경우 양수로 변환
- 양수인 경우 그대로 반환
정답
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와 상관 없음)
Reference
[Medium] 531. String to Union
View on GitHub: https://tsch.js.org/531
문제
문자열 인수를 입력받는 String to Union 유형을 구현하세요. 출력은 입력 문자열의 Union type이어야 합니다.
문제 설명
- 문자열 인수를 입력 받을 경우, 각 문자를 유니온 타입으로 반환하는 문제
- 문자열이 비어있는 경우 never를 반환
정답
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 참고)
[Medium] 599. Merge
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로 처리됨
추가 질문
왜 중복되는 키의 경우, value가 never로 처리되는지?
- 인터섹션 타입의 경우, 동일한 키가 충돌할 경우에는 별도의 규칙이 적용됨
- 예를 들어,
{ a: number } & { a: string }의 경우,{ a: number & string }로 처리됨 - 이 경우,
a의 타입은number & string이 되며, 이는 유효하지 않은 타입이므로never로 처리됨
[Medium] 612. KebabCase
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를 반환하여-를 붙임
comments
loading…