8767 - Combination
Given an array of strings, do Permutation & Combination.
It's also useful for the prop types like video controlsList
/* _____________ 여기에 코드 입력 _____________ */ type Combination<T extends string[], U = T[number], I = U> = I extends string ? I | `${I} ${Combination<[], Exclude<U, I>>}` : never; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Combination<['foo', 'bar', 'baz']>, 'foo' | 'bar' | 'baz' | 'foo bar' | 'foo bar baz' | 'foo baz' | 'foo baz bar' | 'bar foo' | 'bar foo baz' | 'bar baz' | 'bar baz foo' | 'baz foo' | 'baz foo bar' | 'baz bar' | 'baz bar foo'>>, Expect<Equal<Combination<['apple', 'banana', 'cherry']>, 'apple' | 'banana' | 'cherry' | 'apple banana' | 'apple cherry' | 'banana apple' | 'banana cherry' | 'cherry apple' | 'cherry banana' | 'apple banana cherry' | 'apple cherry banana' | 'banana apple cherry' | 'banana cherry apple' | 'cherry apple banana' | 'cherry banana apple'>>, Expect<Equal<Combination<['red', 'green', 'blue', 'yellow']>, 'red' | 'green' | 'blue' | 'yellow' | 'red green' | 'red blue' | 'red yellow' | 'green red' | 'green blue' | 'green yellow' | 'blue red' | 'blue green' | 'blue yellow' | 'yellow red' | 'yellow green' | 'yellow blue' | 'red green blue' | 'red green yellow' | 'red blue green' | 'red blue yellow' | 'red yellow green' | 'red yellow blue' | 'green red blue' | 'green red yellow' | 'green blue red' | 'green blue yellow' | 'green yellow red' | 'green yellow blue' | 'blue red green' | 'blue red yellow' | 'blue green red' | 'blue green yellow' | 'blue yellow red' | 'blue yellow green' | 'yellow red green' | 'yellow red blue' | 'yellow green red' | 'yellow green blue' | 'yellow blue red' | 'yellow blue green' | 'red green blue yellow' | 'red green yellow blue' | 'red blue green yellow' | 'red blue yellow green' | 'red yellow green blue' | 'red yellow blue green' | 'green red blue yellow' | 'green red yellow blue' | 'green blue red yellow' | 'green blue yellow red' | 'green yellow red blue' | 'green yellow blue red' | 'blue red green yellow' | 'blue red yellow green' | 'blue green red yellow' | 'blue green yellow red' | 'blue yellow red green' | 'blue yellow green red' | 'yellow red green blue' | 'yellow red blue green' | 'yellow green red blue' | 'yellow green blue red' | 'yellow blue red green' | 'yellow blue green red'>> , Expect<Equal<Combination<['one', 'two']>, 'one' | 'two' | 'one two' | 'two one'>>, ]
풀이
- 이전 컴비네이션과 같이, 전체 튜플을 유니온 화 한
U
타입과 특정I
1개를 선택 해 빼는 방식으로 분배
I
extendsstring
을 통해 1개 씩I
가 분배되도록 한다.
배운점
U = T[number]
를 통해 튜플 내 모든 요소를 유니온으로 만들 수 있다.
type Tuple = [1,2,3]; type U = Tuple[number]; // 1 | 2 | 3
- controlsList
html video, audio 요소에서 사용되는 속성으로, 비디오나 오디오 요소 안의 특정 기본 컨트롤 버튼을 숨길 수 있도록 만들어져있다.
const audio = document.createElement("audio"); audio.controlsList = "nodownload";
- DOMTokenList
DOM 속성에 들어가는
Element.classList
나 HTMLLinkElement.relList
등 문자열을 공백과 나눈 토큰이다. class
속성 등 문자열과 공백으로 나눈 속성값을 해석하기 위해 만들어진 웹 API 인터페이스<p class="a b c"></p>
let para = document.querySelector("p"); let classes = para.classList; para.classList.add("d"); para.textContent = `paragraph classList is "${classes}"`;
tailwind 는 따로 파서가 있다고 함
- 풀이 중 이해할 수 없던 현상
type TwoCombination<A extends string, B extends string> = A | B | `${A} ${B}` | `${B} ${A}`; type Combination<T extends string[]> = T extends [infer F extends string, ...infer R extends string[]] ? TwoCombination<F, Combination<R>> : never; // "a" | TwoCombination<"b", "c"> | "a b" | "a c" | "a b c" | "a c b" | "b a" | "c a" | "b c a" | "c b a" type Test = Combination<['a', 'b', 'c']>
왜
TwoCombination
이 “b” | “c” | “b c” | “c b”
로 해석되지 않고 타입 그대로로 해석 되었는가?- 타입스크립트 타입 시스템은 Lazy 하게 타입을 평가하기 때문
8987 - Subsequence
Given an array of unique elements, return all possible subsequences.
A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements.
type A = Subsequence<[1, 2]> // [] | [1] | [2] | [1, 2]
/* _____________ 여기에 코드 입력 _____________ */ type Subsequence<T extends any[]> = T extends [infer F, ...infer R extends any[]] ? [F] | [...Subsequence<R>] | [F, ...Subsequence<R>] : []; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Subsequence<[1, 2]>, [] | [1] | [2] | [1, 2]>>, Expect<Equal<Subsequence<[1, 2, 3]>, [] | [1] | [2] | [1, 2] | [3] | [1, 3] | [2, 3] | [1, 2, 3]>>, Expect<Equal<Subsequence<[1, 2, 3, 4, 5]>, [] | [1] | [2] | [3] | [4] | [5] | [1, 2] | [1, 3] | [1, 4] | [1, 5] | [2, 3] | [2, 4] | [2, 5] | [3, 4] | [3, 5] | [4, 5] | [1, 2, 3] | [1, 2, 4] | [1, 2, 5] | [1, 3, 4] | [1, 3, 5] | [1, 4, 5] | [2, 3, 4] | [2, 3, 5] | [2, 4, 5] | [3, 4, 5] | [1, 2, 3, 4] | [1, 2, 3, 5] | [1, 2, 4, 5] | [1, 3, 4, 5] | [2, 3, 4, 5] | [1, 2, 3, 4, 5] >>, Expect<Equal<Subsequence<['a', 'b', 'c']>, [] | ['a'] | ['b'] | ['c'] | ['a', 'b'] | ['a', 'c'] | ['b', 'c'] | ['a', 'b', 'c'] >>, Expect<Equal<Subsequence<['x', 'y']>, [] | ['x'] | ['y'] | ['x', 'y'] >>, ]
풀이
- 유니온은 중복을 알아서 제거하므로 경우의 수 다 넣음
9142 - CheckRepeatedChars
Implement type
CheckRepeatedChars<S>
which will return whether type S
contains duplicated chars?type CheckRepeatedChars<'abc'> // false type CheckRepeatedChars<'aba'> // true
/* _____________ 여기에 코드 입력 _____________ */ type CheckRepeatedChars<T extends string, U = never> = `${T}` extends `${infer F}${infer R}` ? F extends U ? true : CheckRepeatedChars<R, U | F> : false; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<CheckRepeatedChars<'abc'>, false>>, Expect<Equal<CheckRepeatedChars<'abb'>, true>>, Expect<Equal<CheckRepeatedChars<'cbc'>, true>>, Expect<Equal<CheckRepeatedChars<''>, false>>, ]
풀이
- 리터럴 타입은 유니온으로 필터링이 가능하기 때문에 하나하나 잘라가며 유니온필터
U
에 쌓고 확인하는 방식
9286 - FirstUniqueCharIndex
Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1. (Inspired by leetcode 387)
/* _____________ 여기에 코드 입력 _____________ */ type Includes<T extends string, C extends string> = `${C}` extends `${infer _}${T}${infer _}` ? true : false; type IncludesTest = Includes<"o", 'olee'>; type FirstUniqueCharIndex<T extends string, H extends string = '', I extends never[] = []> = `${T}` extends `${infer F}${infer R}` ? Includes<F, `${H}${R}`> extends true ? FirstUniqueCharIndex<R, `${H}${F}`, [...I, never]> : I['length'] : -1; type Test = FirstUniqueCharIndex<'loveleetcode'>;
풀이
Includes
타입은C
문자열에T
문자가 존재하는지 할 수 있다.
- 이를 통해 탐색을 이미 한 문자를
H
에 쌓아가며 합쳐Includes
타입을 통해 검증
I
를 통해 인덱스 리턴
9616 - Parse URL Params
You're required to implement a type-level parser to parse URL params string into an Union.
type Test1 = ParseUrlParams<':id'> // id type Test2 = ParseUrlParams<'posts/:id'> // id type Test3 = ParseUrlParams<'posts/:id/:user'> // id | user
/* _____________ 여기에 코드 입력 _____________ */ type ParseParams<Params extends string> = `${Params}` extends `:${infer Param}/${infer R}` ? Param | ParseParams<R> : `${Params}` extends `:${infer P}` ? P : never; type ParseUrlParams<T extends string> = `${T}` extends `${infer _}/${infer Params}` ? ParseParams<Params> : ParseParams<T>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<ParseUrlParams<''>, never>>, Expect<Equal<ParseUrlParams<':id'>, 'id'>>, Expect<Equal<ParseUrlParams<'posts/:id'>, 'id'>>, Expect<Equal<ParseUrlParams<'posts/:id/'>, 'id'>>, Expect<Equal<ParseUrlParams<'posts/:id/:user'>, 'id' | 'user'>>, Expect<Equal<ParseUrlParams<'posts/:id/:user/like'>, 'id' | 'user'>>, ]
풀이
- url 부분과 path param 부분 나눈 후 path param 부분 중에서
:
가 붙어있는걸 가져오는 방식 - 두번째 케이스인
:id
때문에 이렇게 굳이 나눴음
ParseParam
타입에서 예외 케이스인 부분 따로 로직 추가
배운점
type ParseUrlParams<T> = T extends `${string}:${infer R}` ? R extends `${infer P}/${infer L}` ? P | ParseUrlParams<L> : R : never
답지를 보니 이런 깔끔한 방법도 있었음
- 결국
:
뒤의 어떤 것을 구하는 것 이므로,:
를 먼저 검사
R
이/
에 의해 더 나뉘어 질 수 없다면 리턴 아니라면 재귀
9896 - GetMiddleElement
Get the middle element of the array by implementing a
GetMiddleElement
method, represented by an array- If the length of the array is odd, return the middle element
- If the length of the array is even, return the middle two elements
type simple1 = GetMiddleElement<[1, 2, 3, 4, 5]>, // expected to be [3] type simple2 = GetMiddleElement<[1, 2, 3, 4, 5, 6]> // expected to be [3, 4]
/* _____________ 여기에 코드 입력 _____________ */ type IsEven<T extends number, C extends never[] = [], R extends boolean = true> = C['length'] extends T ? R : IsEven<T, [...C, never], R extends true ? false : true>; type IsEvenTest = IsEven<999>; type IsEvenTest2 = IsEven<998>; type SmallMinusOne<T extends number, C extends never[] = []> = C['length'] extends T ? C extends [infer _, ...infer R] ? R['length'] : never : SmallMinusOne<T, [...C, never]>; type SmallMinusOneTest = SmallMinusOne<999>; type SmallPlusOne<T extends number, C extends never[] = []> = C['length'] extends T ? [...C, never]['length'] : SmallPlusOne<T, [...C, never]>; type SmallPlusOneTest = SmallPlusOne<999>; type GetMiddleElementForOdd<T extends unknown[], Forward extends number = 0, Backward extends number = SmallMinusOne<T['length']>> = Forward extends Backward ? [T[Forward]] : GetMiddleElementForOdd<T, SmallPlusOne<Forward>, SmallMinusOne<Backward>>; type GetMiddleElementForOddTest = GetMiddleElementForOdd<[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]>; type GetMiddleElementForEven<T extends unknown[], Forward extends number = 0, Backward extends number = SmallMinusOne<T['length']>> = T['length'] extends 0 ? [] : Forward extends SmallMinusOne<Backward> ? [T[Forward], T[Backward]] : GetMiddleElementForEven<T, SmallPlusOne<Forward>, SmallMinusOne<Backward>>; type GetMiddleElementForEvenTest = GetMiddleElementForEven<[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]>; type GetMiddleElement<T extends unknown[]> = IsEven<T['length']> extends true ? GetMiddleElementForEven<T> : GetMiddleElementForOdd<T>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<GetMiddleElement<[]>, []>>, Expect<Equal<GetMiddleElement<[1, 2, 3, 4, 5]>, [3]>>, Expect<Equal<GetMiddleElement<[1, 2, 3, 4, 5, 6]>, [3, 4]>>, Expect<Equal<GetMiddleElement<[() => string]>, [() => string]>>, Expect<Equal<GetMiddleElement<[() => number, '3', [3, 4], 5]>, ['3', [3, 4]]>>, Expect<Equal<GetMiddleElement<[() => string, () => number]>, [() => string, () => number]>>, Expect<Equal<GetMiddleElement<[never]>, [never]>>, ] // @ts-expect-error type error = GetMiddleElement<1, 2, 3>
풀이
- 전체 로직은 앞 뒤 인덱스를 동시에 움직여 가운데 찾는 방식, 원소의 수가 홀수라면 인덱스가 같을 때, 원소의 수가 짝수라면 인덱스가 1 차이 날 때 그 각각의 값들을 리턴
- 이를 위해
IsEven
,GetMiddleElementForEven
,GetMiddleElementForOdd
타입을 나누고,SmallPlusOne
,SmallMinusOne
유틸 타입으로 인덱스 움직이며 로직 구현
배운점
type GetMiddleElement<T extends any[]> = T['length'] extends 0 | 1 | 2? T: T extends [any,...infer M,any]? GetMiddleElement<M>:never
답지를 보니, 양 끝을 동시에 하나씩 제거하는 방식으로 구현…
- 문자열 리터럴 처럼 배열도 양 끝을 한번에 처리 할 수 있다.
type GetMiddleElement<T extends any[]> = T extends [infer F, ...infer R, infer L] ? R extends [] ? [F, L] : GetMiddleElement<R> :