🖥️Frontend/TypeScript

[TypeScript] 공변성, 반공변성

뉴발자 2023. 11. 13.
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TypeScript

 

 

타입의 공변성, 반공변성

타입 스크립트 강의나 블로그를 보면 공변성, 반공변성 이란 단어가 등장하게 된다.

 

처음 들어보는 단어라 블로그 예시를 찾아보았다.


공변성(Covariance)

A가 B의 서브타입이면, T<A>는 T<B>의 서브타입이다.

 

반공변성(Contravariance)

A가 B의 서브타입이면, T<B>는 T<A>의 서브타입이다.

 

이변성(Bivariance)

A가 B의 서브타입이면, T<A> → T<B>도 되고, T<B> → T<A>도 된다.

 

불변성(Immutability)

A가 B의 서브타입이더라도, T<A> → T<B>도 안되고, T<B> → T<A>도 안된다.


 

이는 타입 스크립트에서 타입 간에 서로 대입을 할 때 되는 경우와 안되는 경우를 파악하기 위해 알아두어야 하는 단어이다.

 

타입 스크립트를 사용하다보면 어떤 타입은 다른 타입에 들어가는데 어떤 타입은 안들어가고 에러가 발생하는 경우가 종종 있다.

 

이는 앞서 말한 공변성, 반공변성과 관련이 있다.

 

타입 스크립트에서는 기본적으로 공변성 규칙을 따르지만, 유일하게 함수의 매개변수는 반공변성의 규칙을 따른다.

 

단, tsconfig의 strictFunctionTypes 옵션이 true인 경우에만 해당한다.

 

만일 strict 모드가 아니라면 함수의 매개변수는 이변성을 갖고 있다.

 

728x90

 

 

공변성

A 타입이 좁고, 넓은 타입 B의 서브타입이면, T<A>는 T<B>의 서브타입이다.

 

타입 스크랩트에서 대다수의 일반적인 경우에 공변성 규칙을 따르게 된다.

 

아래는 공변성 규칙이 적용된 간단한 코드이다.

let strArr: Array<string> = [];
let arr: Array<string | number> = [];

strArr = arr; // string | number 배열안에 string 배열이 포함돼 있으므로 정상코드
arr = strArr; // Error, string 배열안에 number 배열을 넣을 수 없으므로 오류 발생

// 에러 메시지 : 'string | number' 형식은 'string' 형식에 할당할 수 없습니다.

 

string은 유니온 string | number 코드의 서브타입이기 때문에 string 배열을 string | number 배열에 넣는 것이 가능하다.

 

하지만 반대의 경우는 공변성 규칙이 성립되지 않아 에러가 발생하게 된다.

 

쉽게 설명하면 공변성이란 좁은 타입이 넓은 타입 안에 포함되는지를 판단하는 규칙이다.

 

 

반공변성

A 타입이 좁고, 넓은 타입 B의 서브타입이면, T<B>는 T<A>의 서브타입이다.

 

함수의 매개변수는 반공변성의 규칙을 따른다.

type Type<T> = (param: T) => void;

let A: Type<number> = (param) => {
  console.log(param); // number
};

let B: Type<string | number> = (param) => {
  console.log(param); // string | number
};

A = B; // 매개변수는 반공변성 규칙을 따르기 때문에 정상코드
B = A; // Error, 반공변성 규칙에 어긋나기 때문에 에러 발생

// 에러 메시지 : 'string' 형식은 'number' 형식에 할당할 수 없습니다.

 

함수의 매개변수는 반공변성의 규칙을 따르기 때문에 공변성과는 반대의 특징을 가지고 있다.

 

쉽게 설명하면 반공변성은 넓은 타입이 좁은 타입 안에 포함되는지를 판단하는 규칙이다.

 

 

함수에서의 공변성, 반공변성

타입 스크립트에서 함수는 아래의 규칙을 따른다.


  • 함수의 리턴타입은 공변성 규칙을 따른다.
  • 함수의 매개변수는 반공변성 규칙을 따른다.

 

 

함수의 매개변수

function A(x: string): number {
  return +x;
;

type B = (x: string | number) => number;
type C: B = A; // Error, 반공변성 (넓은타입 -> 좁은타입) 규칙에 의해 에러 발생

// 에러 메시지 : 'number' 형식은 'string' 형식에 할당할 수 없습니다.

 

리턴타입은 같지만 매개변수 타입이 다른 함수, 타입에서 넓은 타입의 매개변수를 가진 타입 B에 좁은 타입의 매개변수를 가진 함수 A를 대입하는 상황이다.

 

이론상으로 볼 때, 좁은 타입(string)에서 넓은 타입(string | number)으로 대입하는 정상적인 코드라고 생각될 것이다.

 

하지만 매개변수의 반공변성 규칙에 의해 위 코드는 에러가 발생하게 된다.

 

아래의 코드처럼 매개변수 타입을 수정해주면 정상적으로 동작하는 코드가 된다.

function A(x: string | number): number {
  return +x;
;

type B = (x: string) => number;
type C: B = A; // 정상 동작

 

 

함수의 리턴타입

function A(x: string): string | number {
  return +x;
;

type B = (x: string) => number;
type C: B = A; // Error, 넓은 타입을 좁은 타입에 대입하려해서 에러 발생

// 에러 메시지 : 'string' 형식은 'number' 형식에 할당할 수 없습니다.

 

매개변수는 같지만 리턴타입이 다른 함수, 타입에서 좁은 타입의 매개변수를 가진 타입 B에 넓은 타입의 매개변수를 가진 함수 A를 대입하는 상황이다.

 

기본적으로 함수의 리턴타입은 공변성 규칙을 따르기 때문에 에러가 발생하게 된다.

 

아래의 코드처럼 리턴타입을 수정해주면 정상적으로 동작하는 코드가 된다.

function A(x: string): number {
  return +x;
;

type B = (x: string) => string | number;
type C: B = A; // 정상 동작

 

 

두 규칙을 만족하는 코드는 아래와 같다.

function A(x: string | number): number {
  return +x;
};

type B = (x: string) => string | number;
let C: B = A;

// 함수의 매개변수는 반공변성 규칙을 따른다
// 넓은타입 -> 좁은타입
// 함수의 리턴타입은 공변성 규칙을 따른다.
// 좁은타입 -> 넓은타입

 

함수 A의 매개변수 타입 string | number는 type B의 매개변수 타입 string보다 넓은 타입이다.

 

따라서 함수 A의 매개변수(넓은 타입)는 타입 B의 매개변수(좁은 타입)으로 대입이 가능하다. (반공변성)

 

그리고 함수 A의 리턴 타입 number는 type B의 매개변수 string | number보다 좁은 타입이다.

 

따라서 함수 A의 리턴 타입(좁은 타입)은 타입 B의 리턴 타입(넓은 타입)으로 대입이 가능하다. (공변성)

 

 

참고 사이트

https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B3%B5%EB%B3%80%EC%84%B1-%EB%B0%98%EA%B3%B5%EB%B3%80%EC%84%B1-%F0%9F%92%A1-%ED%95%B5%EC%8B%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

 

📘 타입스크립트 공변성 & 반공변성 완벽 이해

타입의 공변성과 반공변성 타입스크립트는 자바스크립트에 타입을 추가해준 라이브러리 이지만, 타입을 다루는 언어이기도 하다. 그래서 어느새 타입 자체를 코딩하고 있는 자신을 발견하기도

inpa.tistory.com

 

https://www.inflearn.com/course/lecture?courseSlug=%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%98%AC%EC%9D%B8%EC%9B%90-1&unitId=122332

 

학습 페이지

 

www.inflearn.com

 

 

 

 

 

 

 

 

 

 

728x90

'🖥️Frontend > TypeScript' 카테고리의 다른 글

[TypeScript] never  (0) 2023.11.09

댓글