
주어진 배열을 특정 길이의 청크로 나누는 문제입니다.
lodash를 아시나요? Chunk는 lodash에서 매우 유용한 함수입니다. 이제 이것을 구현해보겠습니다.
Chunk<T, N>는 두 개의 필수 타입 매개변수를 받습니다. T는 반드시 튜플이어야 하고, N은 반드시 1 이상의 정수여야 합니다.
예시
type exp1 = Chunk<[1, 2, 3], 2>; // 예상 결과: [[1, 2], [3]]
type exp2 = Chunk<[1, 2, 3], 4>; // 예상 결과: [[1, 2, 3]]
type exp3 = Chunk<[1, 2, 3], 1>; // 예상 결과: [[1], [2], [3]]
문제 설명
접근 방법
코드
type Chunk<
T extends any[],
N extends Number,
Result extends any[] = [],
Chunk extends any[] = []
> = T extends [infer First, ...infer Rest]
? Chunk["length"] extends N
? Chunk<Rest, N, [...Result, Chunk], [First]>
: Chunk<Rest, N, Result, [...Chunk, First]>
: Chunk["length"] extends 0
? Result
: [...Result, Chunk];
코드 설명
Result는 최종적으로 반환될 배열(Chunk[])Chunk는 청크를 채워나가는 배열T를 재귀적으로 탐색하면서, Chunk에 채워넣기Chunk의 길이가 N과 같아지면, Result에 추가Fill은 JavaScript의 일반적인 함수입니다. 이제 타입으로 이를 구현해보겠습니다.
Fill<T, N, Start?, End?>에서 볼 수 있듯이, Fill은 네 가지 타입의 매개변수를 받습니다. 이 중 T와 N은 필수 매개변수이고, Start와 End는 선택적 매개변수입니다.
이러한 매개변수들의 요구사항은 다음과 같습니다: T는 반드시 tuple이어야 하고, N은 어떤 타입의 값이든 될 수 있으며, Start와 End는 0보다 크거나 같은 정수여야 합니다.
예시
type exp = Fill<[1, 2, 3], 0>; // 예상 결과: [0, 0, 0]
문제 설명
Fill 함수를 타입으로 구현Fill 함수는 배열 T와 값 N을 받아서, 배열의 일부를 N으로 채우는 함수Start와 End는 선택적 매개변수로, 채우기를 시작할 인덱스와 끝날 인덱스를 지정접근 방법
N으로 채우기Start와 End를 지정하지 않으면, 배열의 처음부터 끝까지 채우기Index 배열을 만들어서 체크Index 배열의 길이가 Start와 같아지면, Switch를 true로 변경Index 배열의 길이가 End와 같아지면, Switch를 false로 변경End 도달하기 전에 T가 끝나면 Result 반환코드
type Fill<
T extends unknown[],
N,
Start extends number = 0,
End extends number = T["length"],
Index extends unknown[] = [],
Switch extends boolean = false,
Result extends any[] = []
> = T extends [infer First, ...infer Rest]
? Switch extends true
? // Switch가 true인 경우(Index가 End보다 클 경우)
Index["length"] extends End
? // 끝났으면 남은 배열 추가(Index가 End와 같아지면 끝나는 것이므로)
[...Result, ...T]
: // 아직 끝나지 않았으면 다음 요소 추가
Fill<Rest, N, Start, End, [...Index, unknown], Switch, [...Result, N]>
: // Switch가 false인 경우(Index가 Start보다 작을 경우)
Index["length"] extends Start
? // Switch가 false인데 Start와 같아졌을 경우
Start extends End
? // Start와 End가 같으면 N으로 변경 X(원래 값 추가)
[...Result, First, ...Rest]
: // Start와 End가 같지 않으면 N으로 변경, Switch를 true로 변경
Fill<Rest, N, Start, End, [...Index, unknown], true, [...Result, N]>
: // Switch가 false인데 Start보다 작지 않을 경우 그냥 진행
Fill<Rest, N, Start, End, [...Index, unknown], Switch, [...Result, First]>
: Result;
코드 설명
T를 재귀적으로 탐색하면서 First 요소 확인Switch가 true인 경우,Index 배열의 길이가 End와 같아지면 끝나는 것이므로 남은 배열 추가Switch가 false인 경우,Index 배열의 길이가 Start와 같아지면 채워야되는 요소가 되므로 N으로 변경, Switch를 true로 변경End 도달하기 전에 T가 끝나면 Result 반환문자열 타입을 받아 끝부분의 공백을 제거한 새로운 문자열을 반환하는 TrimRight<T>를 구현하세요.
예시
type Trimed = TrimRight<" Hello World ">; // expected to be ' Hello World'
문제 설명
접근 방법
코드
type TrimRight<S extends string> = S extends `${infer T}${" " | "\n" | "\t"}`
? TrimRight<T>
: S;
코드 설명
S를 재귀적으로 탐색하면서 T 요소 확인T 요소가 whitespaces(' ' | '\n' | '\t')가 들어가는 지 확인whitespaces가 들어가면 재귀whitespaces가 들어가지 않으면 그냥 반환Lodash의 without 함수를 타입 버전으로 구현하세요. Without<T, U>는 배열 T와 숫자 또는 배열 U를 받아서 U의 요소들을 제외한 배열을 반환합니다.
예시
type Res = Without<[1, 2], 1>; // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]>; // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>; // expected to be []
문제 설명
T와 숫자 또는 배열 U를 받아서 U의 요소들을 제외한 배열을 반환U는 숫자 또는 배열이 들어올 수 있음접근 방법
T를 재귀적으로 탐색하면서 U의 요소들을 제외한 배열을 반환코드
type TupleToUnion<T extends readonly number[] | number> = T extends number[]
? T[number]
: T;
type Without<T extends any[], U extends number | number[]> = T extends [
infer First,
...infer Rest
]
? First extends TupleToUnion<U>
? [...Without<Rest, U>]
: [First, ...Without<Rest, U>]
: T;
코드 설명
TupleToUnion<U>는 U가 튜플일 경우 유니온 타입으로 변환Without<T, U>는 배열 T를 재귀적으로 탐색하면서 U의 요소들을 제외한 배열을 반환문자열이나 숫자를 받아서 소수점 이하 자릿수를 제거하여 정수 부분만 반환하는 Math.trunc의 타입 버전을 구현하세요.
예시:
type A = Trunc<12.34>; // 12
접근 방법
number일 경우 string으로 변환코드
type Trunc<
T extends number | string,
S = `${T}`
> = S extends `${infer First}.${infer _}` ? `${First}` : S;
실패 원인
.3, -.3 등의 예외 케이스 처리 실패접근 방법
infer로 추론한 First가 빈 문자열이거나 -일 경우 0으로 처리코드
type Trunc<
T extends number | string,
S = `${T}`
> = S extends `${infer First}.${infer _}`
? First extends "" | "-"
? `${First}0`
: First
: S;
코드 설명
S는 T를 문자열로 변환한 결과S가 .를 포함할 경우, First가 빈 문자열이거나 -일 경우 First+0으로 처리First를 그대로 반환S가 .를 포함하지 않을 경우, S를 그대로 반환배열의 Array.indexOf 메서드의 타입 버전을 구현하세요. indexOf<T, U>는 배열 T와 임의의 값 U를 받아서 배열 T에서 처음 등장하는 U의 인덱스를 반환합니다.
type Res = IndexOf<[1, 2, 3], 2>; // expected to be 1
type Res1 = IndexOf<[2, 6, 3, 8, 4, 1, 7, 3, 9], 3>; // expected to be 2
type Res2 = IndexOf<[0, 0, 0], 2>; // expected to be -1
접근 방법
index를 계산하는 어레이를 하나 둬서 length로 어레이 값 리턴infer로 하나씩 확인하며 index Array에 요소 하나씩 증가코드
type IndexOf<T extends any[], U, IndexArr extends unknown[] = []> = T extends [
infer First,
...infer Rest
]
? First extends U
? IndexArr["length"]
: IndexOf<Rest, U, [...IndexArr, unknown]>
: -1;
실패 원인
number와 같은 타입, 그리고 any를 제대로 처리하지 못함First extends U 대신 더 강력한 타입비교를 만들어야 할 듯접근 방법
MyEqual 타입을 가져와서 First extends U 대신 사용코드
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
type IndexOf<T extends any[], U, IndexArr extends unknown[] = []> = T extends [
infer First,
...infer Rest
]
? MyEqual<First, U> extends true
? IndexArr["length"]
: IndexOf<Rest, U, [...IndexArr, unknown]>
: -1;