Given a tuple type T
that only contains string type, and a type U
, build an object recursively.
주어진 튜플 타입 T
가 문자열 타입만 포함하고 있고, 타입 U
가 주어졌을 때, 재귀적으로 객체를 만드세요.
예시
type a = TupleToNestedObject<["a"], string>; // {a: string}
type b = TupleToNestedObject<["a", "b"], number>; // {a: {b: number}}
type c = TupleToNestedObject<[], boolean>; // boolean. if the tuple is empty, just return the U type
접근 방식
코드
type TupleToNestedObject<T extends any[], U> = T extends []
? U
: T extends [infer First, ...infer Rest]
? {First: TupleToNestedObject<Rest, U>}
: never;
]
실패 이유
{First: TupleToNestedObject<Rest, U>}
에서 사용된 First
가 infer
로 추론된 First
값이 아닌 문자열 'First'
로 추론됨접근 방식
코드
type TupleToNestedObject<T extends string[], U> = T extends [
infer First extends string,
...infer Rest extends string[]
]
? { [K in First]: TupleToNestedObject<Rest, U> }
: U;
코드 설명
infer First extends string, ...infer Rest extends string[]
형태로 튜플의 First와 Rest를 나눠서 재귀적으로 처리[K in First]: TupleToNestedObject<Rest, U>
형태로 키값을 First로 받고, 나머지 부분을 재귀적으로 처리튜플을 뒤집는 타입을 구현하세요.
예시
type a = Reverse<["a", "b"]>; // ['b', 'a']
type b = Reverse<["a", "b", "c"]>; // ['c', 'b', 'a']
접근 방식
코드
type Reverse<T extends any[]> = T extends [infer First, ...infer Rest]
? [...Reverse<Rest>, First]
: T;
코드 설명
T extends any[]
형태로 튜플 타입 제어T extends [infer First, ...infer Rest]
형태로 튜플의 First와 Rest를 나눠서 재귀적으로 처리[...Reverse<Rest>, First]
형태로 Rest를 뒤집은 결과(재귀)에 First를 추가lodash의 _.flip
함수를 타입으로 구현하세요.
FlipArguments<T>
타입은 함수 타입 T를 요구하며, 동일한 반환 타입을 가지지만 매개변수가 반대로 된 새로운 함수 타입을 반환합니다.
예시
type Flipped = FlipArguments<
(arg0: string, arg1: number, arg2: boolean) => void
>;
// (arg0: boolean, arg1: number, arg2: string) => void
접근 방식
T
의 args
를 순회하며 각각의 arg type을 저장한 뒤, reverse
해서 다시 할당해보자.3192-reverse
문제를 참고코드
type Reverse<T extends any[]> = T extends [infer First, ...infer Rest]
? [...Reverse<Rest>, First]
: T;
type FlipArguments<T extends (...args: any[]) => any> = T extends (
...args: infer Args
) => infer ReturnType
? (...args: Reverse<Args>) => ReturnType
: never;
코드 설명
T extends (...args: any[]) => any
형태로 함수 타입 제어...args: infer Args
형태로 함수의 인자 타입을 추론 (인자는 튜플 타입으로 추론됨)(...args: Reverse<Args>) => ReturnType
형태로 인자를 뒤집은 뒤, 반환 타입을 그대로 반환never
타입 반환배열을 주어진 깊이까지 재귀적으로 평탄화하는 타입을 구현하세요.
예시
type a = FlattenDepth<[1, 2, [3, 4], [[[5]]]], 2>; // [1, 2, 3, 4, [5]]. flattern 2 times
type b = FlattenDepth<[1, 2, [3, 4], [[[5]]]]>; // [1, 2, 3, 4, [[5]]]. Depth defaults to be 1
접근 방식
코드
/* _____________ 기존 코드 활용 _____________ */
// 한 자리 수의 마이너스 1 처리용 타입
type CalcMinusOne = {
[key: string]: string;
"1": "0";
"2": "1";
"3": "2";
"4": "3";
"5": "4";
"6": "5";
"7": "6";
"8": "7";
"9": "8";
"0": "9";
};
// 문자열 배열(튜플)의 마지막 요소 추출
type Last<T extends string[]> = T extends [...infer _, infer Last]
? Last
: never;
// 문자열을 튜플로 변환
type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
? [First, ...StringToTuple<Last>]
: [];
// 튜플을 문자열로 변환
type Join<T extends string[]> = T extends [
infer First extends string,
...infer Rest extends string[]
]
? `${First}${Join<Rest>}`
: "";
// 마지막 요소를 제거한 튜플을 문자열로 변환
type RemoveLast<T extends string[]> = T extends [
...infer Rest extends string[],
infer _
]
? Join<Rest>
: "";
// 문자열을 숫자로 변환
type StringToNumber<S extends string> = S extends `${infer N extends number}`
? N
: never;
// 문자열 배열(튜플)의 마지막 요소가 0인 경우, 마지막 요소를 제거한 문자열 배열(튜플)을 문자열로 변환
// 만약 마지막 요소가 0이면 9로 변환, 그리고 앞 요소에 대해서도 재귀 돌리기
type MinusOneInStringArray<
S extends string,
ST extends string[] = StringToTuple<S>
> = Last<ST> extends "0"
? S extends "10"
? "9"
: `${MinusOneInStringArray<RemoveLast<ST>>}9`
: Last<ST> extends "_"
? `${MinusOneInStringArray<RemoveLast<ST>>}_`
: `${RemoveLast<ST>}${CalcMinusOne[Last<ST>]}`;
// 재귀 돌리기 전처리
type MinusOne<
T extends number,
S extends string[] = StringToTuple<`${T}`>
> = Last<S> extends "0"
? StringToNumber<`${MinusOneInStringArray<RemoveLast<S>>}9`>
: StringToNumber<`${RemoveLast<S>}${CalcMinusOne[Last<S>]}`>;
// Flatten 함수(1회만 실행하는)
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...First, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
/* _____________ Your Code Here _____________ */
type FlattenDepth<T extends any[], N extends number = 1> = N extends 0
? T
: FlattenDepth<Flatten<T>, MinusOne<N>>;
실패 이유
접근 방식
코드
type FlattenDepth<T extends any[], N extends number = 1> = N extends 0
? T
: T extends (string | number | symbol)[]
? T
: FlattenDepth<Flatten<T>, MinusOne<N>>;
코드 설명
N extends 0
형태로 N이 0이 될 때까지 재귀 호출T extends (string | number | symbol)[]
형태로 이미 flatten된 배열인 경우 재귀 끝내기FlattenDepth<Flatten<T>, MinusOne<N>>
형태로 flatten 함수를 재귀적으로 호출더 나은 방법
CountArray['length']
)를 비교하여 재귀 호출 중단Block, Element, Modifier 방법론(BEM)은 CSS에서 클래스 명명에 널리 사용되는 규칙입니다.
예를 들어, 블록 컴포넌트는 btn
으로 표현되고, 블록에 종속된 엘리먼트는 btn__price
로 표현되며, 블록의 스타일을 변경하는 수정자는 btn--big
또는 btn__price--warning
으로 표현됩니다.
BEM<B, E, M>
을 구현하세요. 이는 세 개의 매개변수로부터 문자열 유니온을 생성합니다. 여기서 B
는 문자열 리터럴이고, E
와 M
은 문자열 배열입니다(비어있을 수 있음).
예시
type a = BEM<"btn", ["price"], []>; // 'btn__price'
접근 방식
코드
type BEM<
B extends string,
E extends string[],
M extends string[]
> = `${B}${E extends [] ? "" : `__${E[number]}`}${M extends []
? ""
: `--${M[number]}`}`;
코드 설명
E extends [] ? "" :
__${E[number]}`` 형태로 E가 빈 배열인 경우 빈 문자열 반환, 비어있지 않을 경우 순회하여 유니온 요소 각각 생성이진 트리의 중위 순회를 구현하는 타입을 작성하세요.
예시
const tree1 = {
val: 1,
left: null,
right: {
val: 2,
left: {
val: 3,
left: null,
right: null,
},
right: null,
},
} as const;
type A = InorderTraversal<typeof tree1>; // [1, 3, 2]
접근 방식
코드
interface TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
}
type InorderTraversal<T extends TreeNode | null> = T extends TreeNode
? [...InorderTraversal<T["left"]>, T["val"], ...InorderTraversal<T["right"]>]
: [];
코드 설명
T extends TreeNode
형태로 TreeNode 타입인 경우 재귀 호출T extends TreeNode | null
형태로 TreeNode 타입이 아닌 경우 빈 배열 반환[...InorderTraversal<T["left"]>, T["val"], ...InorderTraversal<T["right"]>]
형태로 왼쪽, 밸류값, 오른쪽 순서로 순회