✍ 공부/TypeScript

[type-challenges] AnyOf< {[key:string]:never}와 {} type은 뭐가 다를까?>

Po_tta_tt0 2023. 7. 4. 21:23
반응형

문제

Python의 any function을 타입 시스템으로 구현하세요

배열을 사용하고 배열의 요소가 참이면 true를 반환합니다. 배열이 비어 있으면 false를 반환합니다

 

설명


/* _____________ Your Code Here _____________ */

type False = 0 | '' | false | [] |undefined | null | {[key:string]:never}

type AnyOf<T extends readonly any[]> = T[number] extends False ? false: true

type t = AnyOf<[0, '', false, [], {}, undefined, null]>
type tt = AnyOf<[1, 'test', true, [1], { name: 'test' }, { 1: 'test' }]>

type temp =  0 extends null ? 'f' : 't'
/* _____________ Test Cases _____________ */
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>>,
]
type False = 0 | '' | false | [] | undefined | null | {}

type AnyOf<T extends readonly any[]> = T[number] extends False ? false: true

이 단계까지는 왔었는데
{[key:string]:never}이 부분이 어려웠다.
일반적인 {}이 아니라 never을 사용한다는 점이 인상깊었는데,
이 작동 방식은
[key: string]으로 문자열 key를 가질 수 있다.
동시에 객체 안의 모든 문자열 key의 value는 never이 된다.

객체의 value가 never이라는 것은 뭘 의미할까?

 

실험을 해보자

type VariantA = {
    a: string
    b: never
}

const temp:VariantA = {
  a:'주현',
  b: 0 // 🚨 ERROR! Type 'number' is not assignable to type 'never'.
}

이렇게 작성하면 에러가 생긴다.
type never에 number을 넣으면 안된다..?

never 타입은 타이핑을 부분적으로 허용하지 않고 비활성화하는 기능을 가지고 있다.
따라서 비활성화된 타입에 number 타입을 넣었으니.. 에러가 생기는 것!

 

그렇다면

type VariantA = {
    a: string
    b?: never
}

const temp:VariantA = {
  a:'주현',
  b: 0 // 🚨 ERROR! Type 'number' is not assignable to type 'undefined'.
}

이렇게는 어떨까?
b는 never | undefined 처럼 동작하여( == undefined) number type인 never은 타입 에러를 발생시킨다

type VariantA = {
    a: string
    b?: never
}

const temp:VariantA = {
  a:'주현',
  b: undefined
}

b에 undefined를 주면 문제를 해결할 수 있다 (VariantA의 b type은 undefined이므로)

 

문제로 돌아가보자

type T = {[key:string]:never}

const temp2: T={
  a:'주현', // 🚨 ERROR! Type 'string' is not assignable to type 'never'.
  b:'주현주현', // 🚨 ERROR! Type 'string' is not assignable to type 'never'.
  c:'', // 🚨 ERROR! Type 'string' is not assignable to type 'never'.
  d:undefined // 🚨 ERROR! Type 'string' is not assignable to type 'never'.
}

우리가 방금 했던 실험을 기반으로 하자면,
type T는 객체 형태의 타입이고,
string인 key를 가지고 있고 value가 never인(타이핑을 부분적으로 허용하지 않고 비활성화된) 타입이다.


따라서, 어떤 내용을 넣더라도 에러를 발생시킨다.
애초에 존재하면 안되는 타입이므로!

type T ={[key:string]:never}type P = {}는 어떻게 다를까?

새로운 궁금증이 생길 수 있다!
value를 never로 선언한 type T와 빈 객체 타입인 type P는 어떻게 다를까?

type T에 대한 설명은 위에서 했으니, type P에 대한 설명만 해보자면

type P = {}

const temp3: P={
  a:'주현',
  b:'주현주현',
  c:'',
  d:undefined,
  e: 0,
  f: null
}

는 문제없이 작동한다.
음, 문제에서 {[key:string]:never} 타입을 이용해야 했던 이유는, 객체 안에 값이 있든 없든 {} 타입은 실제 객체 안에 값이 존재할수도 있는 것을 막지 못하구나!!

정말 마지막으로...

한 발만 더 나아가서 마지막 궁금증을 해결해보자

type P로 정의한 temp3을 이용할 수 있을 것인가?

type P = {}

const temp3: P={
  a:'주현',
  b:'주현주현',
  c:'',
  d:undefined,
  e: 0,
  f: null
}
type temp3Type = typeof temp3 // {}
const temp3A = temp3.a // 🚨 ERROR! Property 'a' does not exist on type 'P'.

에러가 발생한다.
당연하지! temp3의 type은 {}이고, type P에는 a라는 property가 명시되어 있지 않다.😖💦 머쓱..ㅎㅎ

 

 

그래도 여기까지 왔으니, type E도 만들어보자

type E = {
  a:string;
  b:string;
  c:string;
  d:undefined;
  e: number;
  f: null
}

const temp4:E = {
  a:'주현',
  b:'주현주현',
  c:'',
  d:undefined,
  e: 0,
  f: null
}
type temp4Type = typeof temp4
const temp4A = temp4.a

익숙한 모양이죠? 잘 동작한다 😉

반응형