TS 공변성,반공변성,이변성

HYUNGU, KANG·2022년 2월 19일
1

공변성

AB 의 서브 그룹일때
T<A>T<B> 의 서브 그룹이다.
받은 값들을 사용하지 않는 경우, 구분할 필요가 없는 케이스.

// <number> is subgroup of <number | string>
let a:Array<number> = [];
let b:Array<number|string> = [];
a = b; // ERR, 숫자 배열(a)에는 문자(b)가 들어갈 수 없음.
b = a; // OK, 숫자/문자 배열(b)에는 숫자(a)가 들어갈 수 있음.

반공변성

AB 의 서브 그룹일때
T<A>T<B> 가 슈퍼 그룹이다.
받은 값들을 사용해야 하는 경우, 구분할 필요가 있는 케이스.

type fn<T> = (param: T) => void;
// <number> is subgroup of <number | string>
let a:fn<number> = (param) => {};
let b:fn<number|string> = (param) => {};
a = b; // OK, 숫자/문자를 처리하는 함수(b)는 숫자(a)를 처리하는 케이스가 있으므로, 숫자만 처리하는 함수(a)에 대입 가능하다.
b = a; // ERR, 숫자만 처리하는 함수(a)에선 문자(b)를 처리하는 케이스가 없으므로, 문자도 처리하는 함수(b)에 대입이 불가능하다.

이변성

공변성+반공변성, 서브그룹/슈퍼그룹 둘 다 OK

// 제네릭이 들어간 인터페이스의 경우
// 내부적으로 공변성, 반공변성 두가지가 필요한 케이스가 존재할 수 있다.
// 이런 케이스를 위해서 인터페이스 선언 시, 함수의 파라메터의 이변성/반공변성을 선택하는 방법이 존재한다.
interface IBucket<T> {
  items: T[];
  // func(): type; 이렇게 쓰면 이변성 허용.
  canPush(item: T): item is T;
  // func: () => type; 이렇게 쓰면 반공변성.
  push: (item: T) => void;
}

class Bucket implements IBucket<number | string> {
  items: (number | string)[] = [];
  // item 에 대해서 이변성 허용 (제네릭으로 받은 <string|number> 의 서브 그룹, 슈퍼 그룹 허용)
  canPush(item: number | boolean | string | object | undefined | Symbol | any[]): item is number | string {
    const type = typeof item;
    return !!type.match(/string|number/);
  }
  // item 에 대해서 반공변성만 허용 (제네릭으로 받은 <string|number> 만 허용)
  push(item: string | number): void {
    this.items.push(item);
  }
}

이변성에 대한 예시는 좀 극단적인 것 같긴하다.
문제가 있다면 댓글 부탁드립니다.

ref. https://seob.dev/posts/공변성이란-무엇인가

profile
JavaScript, TypeScript and React-Native

0개의 댓글