위 그림에서 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';
만약에 이런 interface와 type 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 alias를 A의 타입으로 추론하고
만약에 함수가 아니라면 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 다음에 오는 타입이 진짜 존재하는 타입인지 확인해서 정확한 타입 추론이 이뤄지게 만들어준다.