본문 바로가기
Language/Typescript

타입챌린지 : 2946-ObjectEntries (medium)

by hsloth 2023. 5. 21.

 

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.

 

https://github.com/type-challenges/type-challenges/blob/main/questions/02946-medium-objectentries/README.md

 

GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

Collection of TypeScript type challenges with online judge - GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

github.com

 

자바스크립트의 Object.entries를 구현하는 타입이다.

자바스크립트에서 Object.entries(obj) 를 실행하면, obj안에 있는 key와 value 값이 Array 형태로 리턴된다.

 

내가 처음으로 작성한 답이다.

type ObjectEntries<T> = keyof T extends infer P ? P extends keyof T ?
	[P, T[P]] : never : never

keyof T extends infer P ? P extends keyof T ? : T의 key값이 P라면,

[P, T[P]] : never : never : [P, T[P]]인 배열을 리턴하고 아니라면 never를 리턴한다.

 

 

하지만, 이렇게 풀 경우 두번째 케이스인 ObjectEntries<Partial<Model>>, ModelEntries>를 만족하지 못한다.

 

그래서 UnPartial이라는 타입을 만들어 Partial타입일 경우 Optional을 해제하도록 코드를 변경했다.

type UnPartial<T> = {
  [P in keyof T]-? : T[P]
}

type ObjectEntries<T> = keyof T extends infer P ? P extends keyof T ? [P, UnPartial<T>[P]] : never : never

하지만 이렇게 할 경우 세 번째 케이스인 ObjectEntries<{ key?: undefined }>, ['key', undefined]>를 만족하지 못한다...

 

결국엔 이 문제는 UnPartial을 어떻게 구현하냐의 문제다.

간단하다.

type UnPartial<T> = Omit<{
  [P in keyof T as T[P] extends undefined ? never : P]-? : T[P]
} & {
  [P in keyof T as T[P] extends undefined ? P : never] : T[P]
}, never>

type ObjectEntries<T, U = UnPartial<T>> = keyof U extends infer P ?
  P extends keyof U ? [P, U[P]] : never : never

/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

interface Model {
  name: string
  age: number
  locations: string[] | null
}

type M = Partial<Model>

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]>>,
]

 

UnPartial

UnPartial을 undefined이 타입이 원래 리턴값인 속성과, 아닌 속성을 구분하여 주면 된다.

[P in keyof T as T[P] extends undefined ? never : P]-? : T[P] : 원래 리턴값이 undefined이 아닌 속성만 Optional을 없애고 리턴한다.

[P in keyof T as T[P] extends undefined ? P : never]: T[P] : 원래 리턴값이 undefined인 속성만 그대로 리턴한다.

그리고 Omit으로 합쳐준다.

 

ObjectEntries

type ObjectEntries<T, U = UnPartial<T>> : ObjectEntries 타입을 지정하는데, U를 T의 UnPartial타입으로 지정한다.

keyof U extends infer P ? P extends keyof U ? : U의 key값을 P라고 하면

[P, U[P]] : never : never : [P, U[P]]를 리턴하고 아니라면 never를 리턴한다.