icon

메티의 블로그

9주차 타입챌린지 스터디
9주차 타입챌린지 스터디

9주차 타입챌린지 스터디

Tags
TypeScript
날짜
Mar 5, 2025
상태
공개

2757 - PartialByKeys

문제: 두 개의 타입 인수 TK를 사용하는 PartialByKeys<T, K>를 구성하세요. K는 옵셔널하며 T의 프로퍼티로 이루어진 유니언 타입을 지정할 수 있습니다. K를 제공하지 않는다면 Partial<T>와 같이 모든 프로퍼티를 옵셔널하게 만들어야 합니다.
/* _____________ 여기에 코드 입력 _____________ */ // type PartialByKeys<T, K> = {[Index in keyof T as (Index extends K ? Index : never)]+?: T[Index]} & {[Index in keyof T as (Index extends K ? never : Index)]: T[Index]} type IterateObject<T> = {[K in keyof T]: T[K]} type PartialByKeys<T, K extends keyof T = keyof T> = IterateObject<{[Index in keyof T as (Index extends K ? Index : never)]+?: T[Index]} & {[Index in keyof T as (Index extends K ? never : Index)]: T[Index]}> type Test = keyof User /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface User { name: string age: number address: string } interface UserPartialName { name?: string age: number address: string } interface UserPartialNameAndAge { name?: string age?: number address: string } type cases = [ Expect<Equal<PartialByKeys<User, 'name'>, UserPartialName>>, Expect<Equal<PartialByKeys<User, 'name' | 'age'>, UserPartialNameAndAge>>, Expect<Equal<PartialByKeys<User>, Partial<User>>>, // @ts-expect-error Expect<Equal<PartialByKeys<User, 'name' | 'unknown'>, UserPartialName>> ]
  • 풀이
    • & 인터섹션 연산은 의도와 다르게 풀이 되므로 Iterate 해주는 타입으로 래핑하는 것이 필요했다.

2759 - RequiredByKeys

문제: Implement a generic RequiredByKeys<T, K> which takes two type argument T and K. K specify the set of properties of T that should set to be required. When K is not provided, it should make all properties required just like the normal Required<T>.
/* _____________ 여기에 코드 입력 _____________ */ type IterateObject<T> = {[K in keyof T]: T[K]} type RequiredByKeys<T, K extends keyof T = keyof T> = IterateObject<{[Index in keyof T as Index extends K ? Index : never]-?: T[Index]} & {[Index in Exclude<keyof T, K>]?: T[Index]}> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface User { name?: string age?: number address?: string } interface UserRequiredName { name: string age?: number address?: string } interface UserRequiredNameAndAge { name: string age: number address?: string } type cases = [ Expect<Equal<RequiredByKeys<User, 'name'>, UserRequiredName>>, Expect<Equal<RequiredByKeys<User, 'name' | 'age'>, UserRequiredNameAndAge>>, Expect<Equal<RequiredByKeys<User>, Required<User>>>, // @ts-expect-error Expect<Equal<RequiredByKeys<User, 'name' | 'unknown'>, UserRequiredName>>, ]
  • 배운 점
    • -? 를 통해 optional 을 제거할 수 있다.
 

2793 - Mutable

문제: Implement the generic Mutable<T> which makes all properties in T mutable (not readonly).
/* _____________ 여기에 코드 입력 _____________ */ type Mutable<T extends object> = {-readonly [K in keyof T]: T[K]} /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Todo1 { title: string description: string completed: boolean meta: { author: string } } type List = [1, 2, 3] type cases = [ Expect<Equal<Mutable<Readonly<Todo1>>, Todo1>>, Expect<Equal<Mutable<Readonly<List>>, List>>, ] type errors = [ // @ts-expect-error Mutable<'string'>, // @ts-expect-error Mutable<0>, ]
  • 배운 점
    • - readonly 로 뺄 수 있음

2852 - OmitByType

문제: From T, pick a set of properties whose type are not assignable to U.
/* _____________ 여기에 코드 입력 _____________ */ type OmitByType<T, U> = {[K in keyof T as T[K] extends U ? never : K]: T[K]} type Test = OmitByType<Model, boolean> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Model { name: string count: number isReadonly: boolean isEnable: boolean } type cases = [ Expect<Equal<OmitByType<Model, boolean>, { name: string, count: number }>>, Expect<Equal<OmitByType<Model, string>, { count: number, isReadonly: boolean, isEnable: boolean }>>, Expect<Equal<OmitByType<Model, number>, { name: string, isReadonly: boolean, isEnable: boolean }>>, ]
  • 배운 점
    • K 자체를 타입 연산의 결과로 사용할 수 있다.
 

2946 - ObjectEntries

문제: Implement the type version of Object.entries
/* _____________ 여기에 코드 입력 _____________ */ type InnerDistributer<T, K extends keyof T = keyof T> = K extends any ? [K, T[K]] : never; export type ObjectEntries<T> = InnerDistributer<T> type Dental = ObjectEntries<Partial<Model>> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Model { name: string age: number locations: string[] | null } type ModelEntries = ['name', string] | ['age', number] | ['locations', string[] | null] type cases = [ Expect<Equal<ObjectEntries<Model>, ModelEntries>>, Expect<Equal<ObjectEntries<Partial<Model>>, ModelEntries>>, Expect<Equal<ObjectEntries<{ key?: undefined }>, ['key', undefined]>>, Expect<Equal<ObjectEntries<{ key: undefined }>, ['key', undefined]>>, Expect<Equal<ObjectEntries<{ key: string | undefined }>, ['key', string | undefined]>>, ]
  • 풀이
    • 배열의 유니온 형태로 return 이 되어야 하므로 분배 조건부 연산이 필요하다.
    • 각 key 와 그에 맞는 타입이 같이 배열에 들어가야하므로 새로운 제네릭 인자 K 를 넣고 기본값으로 keyof T 를 추가한다.
    • K 자리에 뭔가 들어가면 안 되므로, 래퍼 타입을 만든다.
 

3062 - Shift

문제: Implement the type version of Array.shift
/* _____________ 여기에 코드 입력 _____________ */ type Shift<T extends unknown[]> = T extends [infer _, ...infer Rest] ? Rest : T; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ // @ts-expect-error Shift<unknown>, Expect<Equal<Shift<[]>, []>>, Expect<Equal<Shift<[1]>, []>>, Expect<Equal<Shift<[3, 2, 1]>, [2, 1]>>, Expect<Equal<Shift<['a', 'b', 'c', 'd']>, ['b', 'c', 'd']>>, ]