645 Diff
문제:
O
& O1
의 차이점인 객체
를 가져옵니다/* _____________ 여기에 코드 입력 _____________ */ // type Diff<O, O1> = {[index in (keyof O | keyof O1)]: index extends keyof O ? index extends keyof O1 ? never : O[index] : index extends keyof O1 ? O1[index] : never} type Diff<O, O1> = {[index in Exclude<(keyof O | keyof O1), (keyof O & keyof O1)>]: index extends keyof O ? index extends keyof O1 ? never : O[index] : index extends keyof O1 ? O1[index] : never} /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type Foo = { name: string age: string } type Bar = { name: string age: string gender: number } type Coo = { name: string gender: number } type cases = [ Expect<Equal<Diff<Foo, Bar>, { gender: number }>>, Expect<Equal<Diff<Bar, Foo>, { gender: number }>>, Expect<Equal<Diff<Foo, Coo>, { age: string, gender: number }>>, Expect<Equal<Diff<Coo, Foo>, { age: string, gender: number }>>, ]
- 배운 점
- key 순회 할 때 아예 빼는 것과 공집합을 주는 것은 다르다.
extends
로 명확하게 타입 상속을 지정해주지 않으면, 타입스크립트는 해당 타입을 명시하지 못 한다.
949 Any of
문제: Python 의 any of 함수를 구현하세요. (어떤 배열 요소의 값중 하나라도 truthy 한 값이 있는지 판별하는 함수)
/* _____________ 여기에 코드 입력 _____________ */ type isFalsy<T> = T extends 0 | "" | false | undefined | null ? true : // T extends [infer Inner] ? Inner extends never ? true : false : T extends unknown[] ? T['length'] extends 0 ? true : false : keyof T extends never ? true : false; type AnyOf<T extends readonly any[]> = T extends [infer First, ...infer Rest] ? isFalsy<First> extends true ? AnyOf<Rest> : true : false; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<AnyOf<[1, 'test', true, [1], { name: 'test' }, { 1: 'test' }]>, true>>, Expect<Equal<AnyOf<[1, '', false, [], {}]>, true>>, Expect<Equal<AnyOf<[0, 'test', false, [], {}]>, true>>, Expect<Equal<AnyOf<[0, '', true, [], {}]>, true>>, Expect<Equal<AnyOf<[0, '', false, [1], {}]>, true>>, Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }]>, true>>, Expect<Equal<AnyOf<[0, '', false, [], { 1: 'test' }]>, true>>, Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }, { 1: 'test' }]>, true>>, Expect<Equal<AnyOf<[0, '', false, [], {}, undefined, null]>, false>>, Expect<Equal<AnyOf<[]>, false>>, ]
- 배운 점
- falsy 한 값을 판별하기 위해 사용하는 방법으로 다음을 코드 안 방법을 사용한다. 특히 배열은 length 0 으로 한다. 내부 배열을 꺼내서 never 는 안 먹힘
// false type Test = [] extends [infer Inner] ? true : false;
[] extends [infer Inner] ? true : false
는 false 임{}
는 null
과 undefined
값을 제외한 나머지 모든 타입과 조건부 타입 연산 시 true
를 반환 함T[number]
로 순회 할 수 있다.1024 Is never
문제: input type으로
T
를 받는 IsNever type을 구현하세요. 만약 T
의 유형이 never
으로 확인되면 true
를 반환하고 아니면 false
를 반환합니다/* _____________ 여기에 코드 입력 _____________ */ type IsNever<T> = [T] extends [never] ? true : false; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<IsNever<never>, true>>, Expect<Equal<IsNever<never | string>, false>>, Expect<Equal<IsNever<''>, false>>, Expect<Equal<IsNever<undefined>, false>>, Expect<Equal<IsNever<null>, false>>, Expect<Equal<IsNever<[]>, false>>, Expect<Equal<IsNever<{}>, false>>, ]
- 배운 점
- 이전에도 한적 있지만, T 가 never 로 들어오면 분배 조건부 타입 연산시 never 로 들어가 아예 조건부 타입 연산 자체가 누락되어버린다. (분배 조건부 타입의 연산은 우선 유니언 타입의 각 요소들을 기준으로 조건부 타입 연산을 실행하고, 후에 유니언으로 합치는 방식인데, never 가 제네릭으로 들어올 시 유니언으로 합칠때 never 이므로 누락되는 것 처럼 보임) 이를 피하기 위해서는 타입을 재가공하여 비교한다.
1097 Is union
문제:
T
를 입력으로 받고, T
가 Union
유형으로 확인되는지 여부를 반환하는 IsUnion
을 구현하세요/* _____________ 여기에 코드 입력 _____________ */ // type IsUnion<T, C extends unknown[] = []> = [T] extends [never] ? false : T extends infer First | infer Rest ? IsUnion<Rest, [C, First]> : C['length'] extends 1 ? false : true; type Test = IsUnion<'a' | 'b'> type ToArray<T> = [T] extends [any] ? T[] : never; // (string | number)[] type DistributeToArray<T> = T extends any ? T[] : never; // string[] | number[] type IsUnion<T> = [T] extends [never] ? false : ToArray<T> extends DistributeToArray<T> ? false : true; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<IsUnion<string>, false>>, Expect<Equal<IsUnion<string | number>, true>>, Expect<Equal<IsUnion<'a' | 'b' | 'c' | 'd'>, true>>, Expect<Equal<IsUnion<undefined | null | void | ''>, true>>, Expect<Equal<IsUnion<{ a: string } | { a: number }>, true>>, Expect<Equal<IsUnion<{ a: string | number }>, false>>, Expect<Equal<IsUnion<[string | number]>, false>>, // Cases where T resolves to a non-union type. Expect<Equal<IsUnion<string | never>, false>>, Expect<Equal<IsUnion<string | unknown>, false>>, Expect<Equal<IsUnion<string | any>, false>>, Expect<Equal<IsUnion<string | 'a'>, false>>, Expect<Equal<IsUnion<never>, false>>, ]
- 배운 점
- 조건부 타입
infer
시[infer A | infer B]
는 의도대로 유니언을 나누는 동작을 하지 않는다. - 조건부 타입에 유니온 타입이 들어오면, 분배되어 들어온다.
- 풀이
- 분배 조건부 타입과 조건부 타입 변형을 비교해 같으면 유니온 타입이 아니라는 뜻
1130 Replace key
문제: Union type의 key를 대체하는 ReplaceKeys를 구현하세요.
만약 일부 유형에 해당 key가 존재하지 않는다면 대체하지 않습니다. 타입은 세 개의 인자를 받습니다.
/* _____________ 여기에 코드 입력 _____________ */ type ReplaceKeys<U, T, Y> = {[index in keyof U]: index extends T ? index extends keyof Y ? Y[index] : never : U[index]} /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type NodeA = { type: 'A' name: string flag: number } type NodeB = { type: 'B' id: number flag: number } type NodeC = { type: 'C' name: string flag: number } type ReplacedNodeA = { type: 'A' name: number flag: string } type ReplacedNodeB = { type: 'B' id: number flag: string } type ReplacedNodeC = { type: 'C' name: number flag: string } type NoNameNodeA = { type: 'A' flag: number name: never } type NoNameNodeC = { type: 'C' flag: number name: never } type Nodes = NodeA | NodeB | NodeC type ReplacedNodes = ReplacedNodeA | ReplacedNodeB | ReplacedNodeC type NodesNoName = NoNameNodeA | NoNameNodeC | NodeB type cases = [ Expect<Equal<ReplaceKeys<Nodes, 'name' | 'flag', { name: number, flag: string }>, ReplacedNodes>>, Expect<Equal<ReplaceKeys<Nodes, 'name', { aa: number }>, NodesNoName>>, ]
- 배운 점
- 조건부 타입이 꼭 아니더라도, 제네릭이 유니온으로 들어오면 기본적으로 분배되어 동작하는듯
- 아님
- 풀이
- 구현 문제, 특정 key 가 유니언 타입으로 들어오고, 그에 맞는 가이드 객체가 존재하면 그 속성대로 변경해줌
1367 Remove index signature
문제: 객체 유형에서 인덱스 시그니처를 제외하는
RemoveIndexSignature<T>
를 구현하세요/* _____________ 여기에 코드 입력 _____________ */ // type RemoveIndexSignature<T> = {[index in Exclude<keyof T, keyof {[key: keyof any]: any}>]: T[index]} type RemoveIndexSignature<T> = {[index in keyof T as (string extends index ? never : number extends index ? never : symbol extends index ? never : index)]: T[index]} type Test = RemoveIndexSignature<Foo> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type Foo = { [key: string]: any foo(): void } type Bar = { [key: number]: any bar(): void 0: string } const foobar = Symbol('foobar') type FooBar = { [key: symbol]: any [foobar](): void } type Baz = { bar(): void baz: string } type cases = [ Expect<Equal<RemoveIndexSignature<Foo>, { foo(): void }>>, Expect<Equal<RemoveIndexSignature<Bar>, { bar(): void, 0: string }>>, Expect<Equal<RemoveIndexSignature<FooBar>, { [foobar](): void }>>, Expect<Equal<RemoveIndexSignature<Baz>, { bar(): void, baz: string }>>, ]
- 배운 점
- 인덱스 시그니처는 런타임에 타입을 추가하고 싶을 때, 이를 추가 할 수 있도록 제약을 풀어주는 역할을 함
as
타입 단언을 통해 타입을 재정의 해주면, 타입스크립트는 재정의된 타입 기준으로 검사한다.