9898 - Appear only once
Find the elements in the target array that appear only once. For example:input:
[1,2,2,3,3,4,5,6,6,6]
,ouput: [1,4,5]
./* _____________ 여기에 코드 입력 _____________ */ // 노션에서 색 구분이 이상하게 되어 주석처리, 실제 코드에선 사용 // type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false; type Has<T extends any[], U> = T extends [infer F, ...infer R] ? IsEqual<F, U> extends true ? true : Has<R, U> : false; type EleminateElement<T extends any[], U, C extends any[] = []> = T extends [infer F, ...infer R] ? IsEqual<F, U> extends true ? EleminateElement<R, U, C> : EleminateElement<R, U, [...C, F]> : C; type EleminateElementTest = EleminateElement<[1,2,3], 0> type FindEles<T extends any[], U extends any[] = [], C extends any[] = []> = T extends [infer F, ...infer R] ? Has<U, F> extends true ? FindEles<R, [...U, F], EleminateElement<C, F>> : FindEles<R, [...U, F], [...C, F]> : C; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<FindEles<[1, 2, 2, 3, 3, 4, 5, 6, 6, 6]>, [1, 4, 5]>>, Expect<Equal<FindEles<[2, 2, 3, 3, 6, 6, 6]>, []>>, Expect<Equal<FindEles<[1, 2, 3]>, [1, 2, 3]>>, Expect<Equal<FindEles<[1, 2, number]>, [1, 2, number]>>, Expect<Equal<FindEles<[1, 2, number, number]>, [1, 2]>>, ]
풀이
- 리터럴 외의 값도 필터링 할 수 있어야하므로 유니온 필터 대신 튜플로 확인
- 필터용 배열 하나, 리턴용 배열 하나 따로만들고, 필터는 계속 쌓아가고, 리턴은 필터에 걸리면 그 값을 찾아 제거한다.
- 특정 값을 제거하는 타입을 만들어 제거한다. 못 찾으면 그대로 리턴하게 만들었다.
배운점
type FindEles<T extends any[], Duplicates = never> = T extends [ infer F, ...infer R ] ? F extends Duplicates ? FindEles<R, Duplicates> : F extends R[number] ? FindEles<R, Duplicates | F> : [F, ...FindEles<R, Duplicates>] : [];
type IncludesInUnion<U, T> = [U] extends [never] ? false : U extends T ? true : false; type FindEles< T extends unknown[], Duplicates extends unknown[] = [], > = T extends [infer Head, ...infer Tail] ? IncludesInUnion<Duplicates[number] | Tail[number], Head> extends false ? [Head, ...FindEles<Tail, Duplicates>] : FindEles<Tail, [...Duplicates, Head]> : T;
- 다음과 같이 return 값의 복사본을 가지고 있다가
- 필터를 통과 할 때는 복사본과 합치고,
- 필터를 통과 못 할 때는 복사본을 그대로 사용한다
9989 - Count Element Number To Object
With type
CountElementNumberToObject
, get the number of occurrences of every item from an array and return them in an object. For example:type Simple1 = CountElementNumberToObject<[]> // return {} type Simple2 = CountElementNumberToObject<[1,2,3,4,5]> // return { // 1: 1, // 2: 1, // 3: 1, // 4: 1, // 5: 1 // } type Simple3 = CountElementNumberToObject<[1,2,3,4,5,[1,2,3]]> // return { // 1: 2, // 2: 2, // 3: 2, // 4: 1, // 5: 1 // }
/* _____________ 여기에 코드 입력 _____________ */ type SimplePlusOne<N, L extends never[] = []> = N extends number ? L['length'] extends N ? [...L, never]['length'] : SimplePlusOne<N, [...L, never]> : never; type SimplePlusOneTest = SimplePlusOne<1>; type Flattern<T extends unknown[]> = T extends [infer F, ...infer R] ? F extends unknown[] ? [...Flattern<F>, ...Flattern<R>] : [F, ...Flattern<R>] : T; type FlatternTest = Flattern<[1,2,3,[1,2,[3,4,5]]]>; type CountElement<T extends unknown[], C extends Record<PropertyKey, unknown> = {}> = [T] extends [never] ? {} : T extends [infer F extends PropertyKey, ...infer R extends PropertyKey[]] ? F extends keyof C ? CountElement<R, {[K in keyof C]: K extends F ? SimplePlusOne<C[K]> : C[K]}>: CountElement<R, C & Record<F,1>>: C; type CountElementTest = CountElement<[1,2,3,4,1]>; type IterateElement<T> = {[K in keyof T]: T[K]} type CountElementNumberToObject<T extends unknown[]> = IterateElement<CountElement<Flattern<T>>>; type Test = CountElementNumberToObject<[1,2,3,[1,2,3]]> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<CountElementNumberToObject<[1, 2, 3, 4, 5]>, { 1: 1 2: 1 3: 1 4: 1 5: 1 } >>, Expect<Equal<CountElementNumberToObject<[1, 2, 3, 4, 5, [1, 2, 3]]>, { 1: 2 2: 2 3: 2 4: 1 5: 1 }>>, Expect<Equal<CountElementNumberToObject<[1, 2, 3, 4, 5, [1, 2, 3, [4, 4, 1, 2]]]>, { 1: 3 2: 3 3: 2 4: 3 5: 1 }>>, Expect<Equal<CountElementNumberToObject<[never]>, {}>>, Expect<Equal<CountElementNumberToObject<['1', '2', '0']>, { 0: 1 1: 1 2: 1 }>>, Expect<Equal<CountElementNumberToObject<['a', 'b', ['c', ['d']]]>, { 'a': 1 'b': 1 'c': 1 'd': 1 }>>, ]
풀이
- 유니온은 중복을 알아서 제거하므로 경우의 수 다 넣음
배운점
// 틀린 답 type CountElement<T extends unknown[], C extends Record<PropertyKey, unknown> = {}> = [T] extends [never] ? {} : T extends [infer F extends PropertyKey, ...infer R extends PropertyKey[]] ? CountElement<R, {[K in keyof C]: K extends F ? SimplePlusOne<C[K]>: C[K]} & Record<F, 1>> : C; // 둘을 동시에 만족하는 집합은 없으므로 never type Test = {1:1} & {1:2}
type Flatten<T,R extends any[] = []> = T extends [infer F,...infer L]? [F] extends [never]? Flatten<L,R>: F extends any[]? Flatten<L,[...R,...Flatten<F>] > :Flatten<L,[...R,F]> :R type Count< T, R extends Record<string | number,any[]> = {} > = T extends [infer F extends string | number,...infer L]? F extends keyof R? Count<L, Omit<R,F>& Record<F,[...R[F],0] > > : Count<L, R & Record<F,[0]>> :{ [K in keyof R]:R[K]['length'] } type CountElementNumberToObject< T > = Count<Flatten<T>>
다음과 같이 배열을 직접 리턴값에 두고, 어차피 한번 순회 할 거 길이를 넣어주도록 한 풀이가 인상깊었다.
10969 - Integer
Please complete type
Integer<T>
, type T
inherits from number
, if T
is an integer return it, otherwise return never
./* _____________ 여기에 코드 입력 _____________ */ type Integer<T> = number extends T ? never : T extends number ? `${T}` extends `${infer _}.${infer _}` ? never : T : never; type Test = Integer<number> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' let x = 1 let y = 1 as const type cases1 = [ Expect<Equal<Integer<1>, 1>>, Expect<Equal<Integer<1.1>, never>>, Expect<Equal<Integer<1.0>, 1>>, Expect<Equal<Integer<1.000000000>, 1>>, Expect<Equal<Integer<0.5>, never>>, Expect<Equal<Integer<28.00>, 28>>, Expect<Equal<Integer<28.101>, never>>, Expect<Equal<Integer<typeof x>, never>>, Expect<Equal<Integer<typeof y>, 1>>, ]
풀이
number
의 경우를 걸러내야하므로number extends T
를 선행
- 템플릿 리터럴로 확인
배운점
type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never
bigint
란? 타입스크립트 예약어로 BigInt
의 타입이다. 얘는 integer 만 되므로, bigint
타입을 사용해 integer 인지 확인 할 수 있다는 답지도 있었다.16259 - ToPrimitive
Convert a property of type literal (label type) to a primitive type.
type X = { name: 'Tom', age: 30, married: false, addr: { home: '123456', phone: '13111111111' } } type Expected = { name: string, age: number, married: boolean, addr: { home: string, phone: string } } type Todo = ToPrimitive<X> // should be same as `Expected`
/* _____________ 여기에 코드 입력 _____________ */ type ToPrimitive<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends Function ? Function : T extends object ? {[K in keyof T]: ToPrimitive<T[K]>} : never; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type PersonInfo = { name: 'Tom' age: 30 married: false addr: { home: '123456' phone: '13111111111' } hobbies: ['sing', 'dance'] readonlyArr: readonly ['test'] fn: () => any } type ExpectedResult = { name: string age: number married: boolean addr: { home: string phone: string } hobbies: [string, string] readonlyArr: readonly [string] fn: Function } type cases = [ Expect<Equal<ToPrimitive<PersonInfo>, ExpectedResult>>, ]
풀이
- type 을 한번에 가져올 수는 없었으므로, primitive 먼저 일일이 검사 후 object 면 재귀
배운점
type ToPrimitive<T> = T extends object ? ( T extends (...args: never[]) => unknown ? Function : { [Key in keyof T]: ToPrimitive<T[Key]> } ) : ( // valueOf 의 return 값을 infer 로 가져올 수 있다. 이는 primitive 값도 꺼내올 수 있다. T extends { valueOf: () => infer P } ? P : T )
valueOf
도 primitive 타입이어도 사용할 수 있음. 자바스크립트 런타임에서 wrapper 객체로 바꾸어 사용하기 때문이다.17973 - DeepMutable
Implement a generic DeepMutable<T> which make every parameter of an object - and its sub-objects recursively - mutable.
type X = { readonly a: () => 1 readonly b: string readonly c: { readonly d: boolean readonly e: { readonly g: { readonly h: { readonly i: true readonly j: "s" } readonly k: "hello" } } } } type Expected = { a: () => 1 b: string c: { d: boolean e: { g: { h: { i: true j: "s" } k: "hello" } } } } type Todo = DeepMutable<X> // should be same as `Expected`
/* _____________ 여기에 코드 입력 _____________ */ type DeepMutable<T extends object> = {-readonly [K in keyof T]: T[K] extends Function ? T[K] : T[K] extends object ? DeepMutable<T[K]> : T[K] } /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Test1 { readonly title: string readonly description: string readonly completed: boolean readonly meta: { readonly author: string } } type Test2 = { readonly a: () => 1 readonly b: string readonly c: { readonly d: boolean readonly e: { readonly g: { readonly h: { readonly i: true readonly j: 's' } readonly k: 'hello' } readonly l: readonly [ 'hi', { readonly m: readonly ['hey'] }, ] } } } interface DeepMutableTest1 { title: string description: string completed: boolean meta: { author: string } } type DeepMutableTest2 = { a: () => 1 b: string c: { d: boolean e: { g: { h: { i: true j: 's' } k: 'hello' } l: [ 'hi', { m: ['hey'] }, ] } } } type cases = [ Expect<Equal<DeepMutable<Test1>, DeepMutableTest1>>, Expect<Equal<DeepMutable<Test2>, DeepMutableTest2>>, ] type errors = [ // @ts-expect-error DeepMutable<'string'>, // @ts-expect-error DeepMutable<0>, ]
풀이
- 단순히 재귀적으로 readonly 를 지우는 타입인데…
배운점
/** type Test = { a: () => 1; b: string; c: DeepMutable<{ readonly d: boolean; readonly e: { readonly g: { readonly h: { readonly i: true; readonly j: "s"; }; readonly k: "hello"; }; readonly l: readonly ["hi", { readonly m: readonly ["hey"]; }]; }; }>; } */ type Test = DeepMutable<Test2>
Test
타입은 위와 같이 해석되는데, 그래도 정답 판정이 나왔다. 이는 왜 그런걸까?18142 - All
Returns true if all elements of the list are equal to the second parameter passed in, false if there are any mismatches.
type Test1 = [1, 1, 1] type Test2 = [1, 1, 2] type Todo = All<Test1, 1> // should be same as true type Todo2 = All<Test2, 1> // should be same as false
/* _____________ 여기에 코드 입력 _____________ */ // type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false; type All<T extends unknown[], I> = T extends [infer F, ...infer R] ? IsEqual<F, I> extends false ? false : All<R, I> : true; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<All<[1, 1, 1], 1>, true>>, Expect<Equal<All<[1, 1, 2], 1>, false>>, Expect<Equal<All<['1', '1', '1'], '1'>, true>>, Expect<Equal<All<['1', '1', '1'], 1>, false>>, Expect<Equal<All<[number, number, number], number>, true>>, Expect<Equal<All<[number, number, string], number>, false>>, Expect<Equal<All<[null, null, null], null>, true>>, Expect<Equal<All<[[1], [1], [1]], [1]>, true>>, Expect<Equal<All<[{}, {}, {}], {}>, true>>, Expect<Equal<All<[never], never>, true>>, Expect<Equal<All<[any], any>, true>>, Expect<Equal<All<[unknown], unknown>, true>>, Expect<Equal<All<[any], unknown>, false>>, Expect<Equal<All<[unknown], any>, false>>, Expect<Equal<All<[1, 1, 2], 1 | 2>, false>>, ]
풀이
- 비즈니스로직은 어려운게 없었음
Equal
복습 느낌이었던 문제
배운점
// type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false; type All<T extends unknown[], I> = IsEqual<T[number], I>; // Expect<Equal<All<[1, 1, 2], 1 | 2>, false>> -> true 로 뜸
답지 확인 결과 이런 방식도 있었으나,
I
가 유니온 타입으로 들어올 경우에는 케이스가 틀리게 됨// your answers type All<A extends unknown[], Elt> = Equal<Equal<A[number], A[0]> & Equal<A[0], Elt>, true> // Expect<Equal<All<[1|2, 2, 1|2], 1|2>, false>> -> true 로 뜸
답지 중 이런 해답도 있었다. 어느정도 일리 있긴 하지만 이는 반대로 튜플 안 원소가 유니온이고, 중간에 다른 원소가 들어가면 새로운 케이스에서는 틀리게 됨