icon

메티의 블로그

7주차 타입스크립트 스터디

7주차 타입스크립트 스터디

Tags
TypeScript
날짜
Feb 19, 2025
상태
공개

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 임
    • {}nullundefined 값을 제외한 나머지 모든 타입과 조건부 타입 연산 시 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를 입력으로 받고, TUnion 유형으로 확인되는지 여부를 반환하는 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 타입 단언을 통해 타입을 재정의 해주면, 타입스크립트는 재정의된 타입 기준으로 검사한다.
 
다음 글이 없습니다.