typescript의 never와 unknown

심재철
3 min readJul 16, 2020

--

위 그림에서 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로 추론하게 해서 에러를 내뱉게 만든것이다.

결론

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

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

--

--

No responses yet