18220 - Filter
원시 타입 또는 유니온 원시 타입인
Predicate
과 Predicate
의 요소로 포함되는 배열을 반환하고, 배열 T
를 가지는 Filter<T, Predicate>
타입을 구현하세요./* _____________ 여기에 코드 입력 _____________ */ type Filter<T extends any[], P> = T extends [infer F, ...infer R] ? F extends P ? [F, ...Filter<R, P>] : [...Filter<R, P>] : []; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type Falsy = false | 0 | '' | null | undefined type cases = [ Expect<Equal<Filter<[0, 1, 2], 2>, [2]>>, Expect<Equal<Filter<[0, 1, 2], 0 | 1>, [0, 1]>>, Expect<Equal<Filter<[0, 1, 2], Falsy>, [0]>>, ]
풀이
P
는 유니온이 가능한 원시? 타입이므로, 각각 원소들 순회 후 필터 값 일 경우 포함 아니면 미포함 하는 방식의 재귀
21104 - FindAll
Given a pattern string P and a text string T, implement the type
FindAll<T, P>
that returns an Array that contains all indices (0-indexed) from T where P matches./* _____________ 여기에 코드 입력 _____________ */ type FindAll<T extends string, P extends string, C extends number[] = [], I extends never[] = []> = P extends '' ? [] : T extends `${P}${infer _}` ? T extends `${infer _}${infer R}` ? [I['length'], ...FindAll<R, P, C, [...I, never]>] : C : T extends `${infer _}${infer R}` ? FindAll<R, P, C, [...I, never]> : C ; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<FindAll<'Collection of TypeScript type challenges', 'Type'>, [14]>>, Expect<Equal<FindAll<'Collection of TypeScript type challenges', 'pe'>, [16, 27]>>, Expect<Equal<FindAll<'Collection of TypeScript type challenges', ''>, []>>, Expect<Equal<FindAll<'', 'Type'>, []>>, Expect<Equal<FindAll<'', ''>, []>>, Expect<Equal<FindAll<'AAAA', 'A'>, [0, 1, 2, 3]>>, Expect<Equal<FindAll<'AAAA', 'AA'>, [0, 1, 2]>>, ]
풀이
- 일일이 배열 돌면서 풀이하기에는 너무 많은 추가적인 타입이 필요할 것 같다는 생각이 들어서 패턴 매칭을 하되, 문자 하나 씩 확인하도록 함 (마지막 케이스 때문)
I
를 통해 현재 index 확인,C
를 통해 결과 누적
P
가 빈 문자열일 때 처리
T
를 패턴 매칭 후, 다음 문자로 넘어가도록 하고, 문자가 확인 됐다면 해당 Index 를C
에 누적 후C
타입 리턴
배운점
type NormalFindAll< T extends string, S extends string, P extends any[] = [], R extends number[] = [], > = T extends `${string}${infer L}`? T extends `${S}${string}`? NormalFindAll<L,S,[...P,0],[...R,P['length']]> :NormalFindAll<L,S,[...P,0],R> :R type FindAll< T extends string, P extends string, > = P extends ''? []:NormalFindAll<T,P>
답지를 보고 개선점 두가지를 보았다.
infer _
대신string
으로 하는게 더 나았던 것 같다.
- 직접 리턴이 아니라 인자로 넘기는 것이라서 직접 리턴이 되는 형태로 추가하기 보단 인자에 추가하는 방식이 더 나았던 것 같다.
21106 - Combination key type
1. Combine multiple modifier keys, but the same modifier key combination cannot appear.
2. In the
ModifierKeys
provided, the priority of the previous value is higher than the latter value; that is, cmd ctrl
is OK, but ctrl cmd
is not allowed./* _____________ 여기에 코드 입력 _____________ */ type Combs<T extends any[]> = T extends [infer F extends string, ...infer R extends string[]] ? `${F} ${R[number]}` | Combs<R> : never; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type ModifierKeys = ['cmd', 'ctrl', 'opt', 'fn'] type CaseTypeOne = 'cmd ctrl' | 'cmd opt' | 'cmd fn' | 'ctrl opt' | 'ctrl fn' | 'opt fn' type cases = [ Expect<Equal<Combs<ModifierKeys>, CaseTypeOne>>, ]
풀이
- 1개를 선택 후 나머지 1개를 유니온 형태로 뿌려주면 되므로
R[number]
로 유니온으로 뿌려지도록 함
배운점
답지도 거의 다 같았음. 어떤 답안은 제네릭 하나 더 써서 넘기는데, 굳이 그럴 필요가 없음.
modifier keys 란?
키보드 이벤트 처리 할 때 등장하는 용어로, 사용자가 일반 키와 함께 누를 수 있는 보조 키를 말한다고 한다. 단축키를 정의 할 때 이런 용어를 사용한다.
21220 - Permutations of Tuple
Given a generic tuple type
T extends unknown[]
, write a type which produces all permutations of T
as a union.type Test = PermutationsOfTuple<[1, number, unknown]> // Should return: // | [1, number, unknown] // | [1, unknown, number] // | [number, 1, unknown] // | [unknown, 1, number] // | [number, unknown, 1] // | [unknown, number ,1]
/* _____________ 여기에 코드 입력 _____________ */ type PermutationsOfTuple<T extends unknown[], I extends never[] = []> = T extends [] ? [] : I['length'] extends T['length'] ? never : T extends [infer F, ...infer R] ? [F, ...PermutationsOfTuple<R>] | PermutationsOfTuple<[...R, F], [...I, never]> : never /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect, ExpectFalse } from '@type-challenges/utils' type cases = [ Expect<Equal<PermutationsOfTuple<[]>, []>>, Expect<Equal<PermutationsOfTuple<[any]>, [any]>>, Expect<Equal<PermutationsOfTuple<[any, unknown]>, [any, unknown] | [unknown, any]>>, Expect<Equal< PermutationsOfTuple<[any, unknown, never]>, | [any, unknown, never] | [unknown, any, never] | [unknown, never, any] | [any, never, unknown] | [never, any, unknown] | [never, unknown, any] >>, Expect<Equal< PermutationsOfTuple<[1, number, unknown]>, | [1, number, unknown] | [1, unknown, number] | [number, 1, unknown] | [unknown, 1, number] | [number, unknown, 1] | [unknown, number, 1] >>, ExpectFalse<Equal<PermutationsOfTuple<[ 1, number, unknown ]>, [unknown]>>, ]
풀이
I
는T
기준 재귀 할 때, 계속 같은 크기의 배열을 넣어 줄 예정이므로 브레이크 포인트를 잡기 위해 사용
- 순서를 바꿔가며 Permutation 재귀
배운점
T extends [infer F, ...infer R] ? [F, ...PermutationsOfTuple<R>] | PermutationsOfTuple<[...R, F], [...I, never]> : never
혼자 풀이 하면서 가장 고민이었던 부분이 하나 선택 후 나머지 값을 보존하는 방법을 고민을 했었는데,
[…R,F]
와 같이 순서를 변경할 수도 있었음.25170 - Replace First
Implement the type ReplaceFirst<T, S, R> which will replace the first occurrence of S in a tuple T with R. If no such S exists in T, the result should be T.
/* _____________ 여기에 코드 입력 _____________ */ type ReplaceFirst<T extends readonly unknown[], S, R, U extends unknown[] = []> = T extends [infer F, ...infer L] ? F extends S ? [...U, R, ...L] : ReplaceFirst<L, S, R, [...U, F]> : U; type Test = ReplaceFirst<[1,2,3],3,4> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<ReplaceFirst<[1, 2, 3], 3, 4>, [1, 2, 4]>>, Expect<Equal<ReplaceFirst<['A', 'B', 'C'], 'C', 'D'>, ['A', 'B', 'D']>>, Expect<Equal<ReplaceFirst<[true, true, true], true, false>, [false, true, true]>>, Expect<Equal<ReplaceFirst<[string, boolean, number], boolean, string>, [string, string, number]>>, Expect<Equal<ReplaceFirst<[1, 'two', 3], string, 2>, [1, 2, 3]>>, Expect<Equal<ReplaceFirst<['six', 'eight', 'ten'], 'eleven', 'twelve'>, ['six', 'eight', 'ten']>>, ]
풀이
- 확인한 원소를 저장하고(
U
), 확인 되었으면 앞에 붙이는 방식으로 구현
배운점
type ReplaceFirst<T extends readonly unknown[], S, R> = T extends readonly [infer F, ...infer Rest] ? F extends S ? [R, ...Rest] : [F, ...ReplaceFirst<Rest, S, R>] : [];
제네릭으로 저장하면서 관리하지 않고, 위의 답지와 같이 쌓아갈 수도 있었음
25270 - Transpose
The transpose of a matrix is an operator which flips a matrix over its diagonal; that is, it switches the row and column indices of the matrix A by producing another matrix, often denoted by A<sup>T</sup>.
type Matrix = Transpose <[[1]]>; // expected to be [[1]] type Matrix1 = Transpose <[[1, 2], [3, 4]]>; // expected to be [[1, 3], [2, 4]] type Matrix2 = Transpose <[[1, 2, 3], [4, 5, 6]]>; // expected to be [[1, 4], [2, 5], [3, 6]]
/* _____________ 여기에 코드 입력 _____________ */ type Transpose<M extends number[][], I extends never[] = [], J extends never[] = [], T extends number[][] = [], C extends number[] = [], IE = M['length'], JE = M[0]['length']> = J['length'] extends JE ? I['length'] extends IE ? // 종료 T: // J 는 종료, I 는 아직 미종료 Transpose<M, [...I, never], J, T, [...C, M[I['length']][J['length']]]>: I['length'] extends IE ? // J 미종료, I 종료 Transpose<M, [], [...J, never], [...T, C], []>: // 둘다 미종료 Transpose<M, [...I, never], J, T, [...C, M[I['length']][J['length']]]>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Transpose<[]>, []>>, Expect<Equal<Transpose<[[1]]>, [[1]]>>, Expect<Equal<Transpose<[[1, 2]]>, [[1], [2]]>>, Expect<Equal<Transpose<[[1, 2], [3, 4]]>, [[1, 3], [2, 4]]>>, Expect<Equal<Transpose<[[1, 2, 3], [4, 5, 6]]>, [[1, 4], [2, 5], [3, 6]]>>, Expect<Equal<Transpose<[[1, 4], [2, 5], [3, 6]]>, [[1, 2, 3], [4, 5, 6]]>>, Expect<Equal<Transpose<[[1, 2, 3], [4, 5, 6], [7, 8, 9]]>, [[1, 4, 7], [2, 5, 8], [3, 6, 9]]>>, ]
풀이
I
는 원본 행렬 기준 행,J
는 열의 인덱스
C
는 1차원 배열,I
를 순회하면서 쌓을 배열
T
는 2차원 배열, 리턴할 전치행렬,C
를 쌓을 배열
- 기존
I
,J
와 다르게I
는 열,J
는 행 축으로 순회 돌도록 한다.
배운점
type Transpose<M extends number[][],R = M['length'] extends 0?[]:M[0]> = { [X in keyof R]:{ [Y in keyof M]:X extends keyof M[Y]?M[Y][X]:never } }
모든 배열 순회 돌며 뒤집기가 가능했음, 여기서
R
은 x 좌표 순회(행 방향 순회) 를 위해 사용하고, 각 x 좌표마다 y 좌표 순회를 M 에서 꺼내 뒤집은 값을 넣는다.