티스토리 뷰

Typescript

typescript의 unknown과 never 타입

심재철 2020. 7. 16. 14:35

위 그림에서 never는 아주 작은 점, unknown은 전체를 포함하고 있다는것을 잘 기억하자.

unknown

다른 모든 타입들의 슈퍼셋이다.

모든 타입들은 unknown타입이다.

never

다른 모든 타입들의 서브셋이다. 가장 최하위 개념의 타입이다.

따라서, 그 어떤 다른 타입들도 never타입일 수 없다.

never는 never그 자체다.

T | never ⇒ T

 

never는 그 어떤 타입도 아니기 때문에 union을 하더라도 그대로다.

T & unknown ⇒ T

unknown은 모든 타입들의 superset이기 때문에 unknown과 어떤 타입 T를 교집합하면 그대로 T가 나온다.

 

never로 타입 추론 예외를 제거하자.

type Arguments<T> = T extends (...args: infer A) => any ? A : never

T가 함수인경우에만 그 함수에 전달된 argument들의 타입을 추론하는 type alias를 만들었다.

 

여기서 infer A는 뭘까? (링크)

interface Example {
    foo: string
}

type GenericExample<T> = T extends Examlep ? 'foo' : 'bar';

만약에 이런 interfacetype alias가 있다고 해보자.

 

이때 개발자가 오타로 Examlep이라고 적었음에도 불구하고 GenericExample<'helloworld'>가 정상적으로 'foo'라는 타입으로 추론된다. T에 어떤 타입이 들어오든 무조건 'foo'로 추론된다. 이렇게 개발자의 실수를 방지하지 못하게 된다.

 

왜냐면 타입스크립트는 Examlep이라는 오타가 오타인건지 실제 존재하는 타입인지 확인하지 않기 때문이다.

interface Example {
    foo: string
}

type GenericExample<T> = T extends infer Examlep ? 'foo' : 'bar';

이런식으로 infer를 통해 명시적으로 타입을 추론하면 타입스크립트가 Examlep이라는 타입은 없는데? 라면서 컴파일 에러를 내뱉어준다.

 

이제 다시 본론으로 돌아가자.

type Arguments<T> = T extends (...args: infer A) => any ? A : never

만약에 T라고 하는 타입이 (...args: infer A) => any 형태의 함수라면? 

Arguments type aliasA의 타입으로 추론하고

만약에 함수가 아니라면 never타입으로 추론된다.

 

never타입에는 그 어떤 값도 할당할 수 없기 때문에 Arguments<T>의 T부분에 함수가 아닌 타입이 오게 되면 무조건 컴파일 에러가 발생한다. 이런식으로 never를 활용 할 수 있다.

 

type Return<T> = T extends (...args: any[]) => infer R ? R : never

이런 형태도 마찬가지다.

T가 함수라면 그 함수의 리턴값을 추론해서 Return type alias의 타입으로 정하고, 

만약에 T가 함수가 아니라면 never로 추론하게 해서 에러를 내뱉게 만든것이다.

type NonNullable<T> = T extends null | undefined ? never : T;

이 헬퍼타입은 타입스크립트에서 제공하는 유틸성 타입이다. 어떤 union 타입에서 null이나 undefined를 제거해준다. 중요한것은 NonNullable의 T에 들어오는 타입은 반드시 null이나 undefined를 갖는 union 타입이어야 한다는것이다. 

Tnull이나 undefined가 없으면 에러가 발생한다.

 

위 문장은 아래와 같이 변경된다.

// 만약에 T가 A | B 유니온 타입이라면
T extends null | undefined ? never : T;

// 아래와 같이 변경된다.
(A extends null | undefined ? never : A) | (B extends null | undefined ? never : B);

이게 무슨말인지 잘 모르겠다. 예시를 살펴보자.

NonNullable<string | null>
  // T가 string | null 유니온 타입이라면
  == (string extends null | undefined ? never : string) | (null extends null | undefined ? never : null)
	
  // string은 null | undefined가 아니므로 string type
  // null은 null | undefined이므로 never type
  == string | never

  // never와 string의 union은 string이다.
  == string
 

 

 

그래서

NonNullable<string | null> == string

이 된다. (유니온 타입에서 null이 제거 되었다.)

 

 

결론

never는 잘못된 타입이 들어오는걸 방지하는 역할을 하게 만들 수 있다.

조건부 타입에서 infer 키워드의 역할은 infer 다음에 오는 타입이 진짜 존재하는 타입인지 확인해서 정확한 타입 추론이 이뤄지게 만들어준다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함