타입스크립트 4.7에tj Variance Annotation으로 'in', 'out'이 추가되었습니다.
Variance란, covariance(공변성), contravariance(반공변성)을 말하는 것으로,
이전의 포스트에서 다룬적이 있습니다.
변성에 대한 개념이 헷갈리신다면 참고해 주세요!
함수는 기본적으로 인자의 타입에 대해서는 반공변성을, 리턴 타입에 대해서는 공변성을 가지고 있습니다.
예제를 한번 보겠습니다.
첫번째 예시는 인자 타입에 대해서 반공변성을 띄는 것에 대한 것입니다.
class User {
username: string;
constructor(username: string) {
this.username = username;
}
}
class Admin extends User {
isAdmin: boolean;
constructor(username: string) {
super(username);
this.isAdmin = true;
}
}
type IsSubtypeOf<A, B> = A extends B ? true : false;
type SubTypeFunc = (param: User) => void;
type BaseTypeFunc = (param: Admin) => void;
// true
type T1 = IsSubtypeOf<SubTypeFunc, BaseTypeFunc>
두번째 예시는 리턴타입입니다.
인자 타입과 달리, 직관에 위배되지 않고 공변성을 띄고 있는 것을 확인할 수 있습니다.
type SubTypeFunc2 = () => 'hello';
type BaseTypeFunc2 = () => string;
// true
type T2 = IsSubtypeOf<SubTypeFunc2, BaseTypeFunc2>
in
, out
, in out
은 제너릭 타입 인자의 Variance(변성)을 표기하는 Annotation입니다.
즉, 제너릭 타입의 타입 인자의 Variance를 명시적으로 표기하는 것입니다.
// out은 공변성
type Getter<out T> = () => T;
// in은 반공변성
type Setter<in T> = (value: T) => void;
이전 포스트에서 함수 인자 타입의 contravariance를 우회하는 Bivariance hack
에 대해서 소개한 적이 있습니다.
그렇다면, in
, out
키워드를 Bivariance hack처럼 사용해서, 함수타입의 variance를 조작할 수 있을까요?
즉, 다음과 같은 예시가 가능할까요?
type Getter<out T> = (param: T) => void;
그럼 아래와 같이 바로 에러를 마주하게 되십니다..
in
, out
은 Annotation일 뿐이지, Bivariance hack으로는 사용할 수 없습니다.
즉, 명시적으로 표기를 하는 것에 의의가 있을 뿐입니다.
별도의 annotation이 있기 때문에, 프로그래머 입장에서 특정 타입인자가 어느 용도로 사용되는지 쉽게 유추할 수 있다. in
을 사용했다면 파라미터, out
이면 리턴타입이겠죠?
컴파일 속도가 빨라진다고 합니다.