포스트레이아웃
Typescript 공변성과 반공변성
2024-02-22
🏷️ table of Content
객체 메서드 선언시 세가지 방식
객체의 메서드를 선언할때는 세 가지 방식으로 할 수있다.
interface Example {
// 1. 메서드(매개변수) : 반환값
a(): void;
// 2. 메서드:(매개변수) => 반환값
b: () => void;
// 3. 메서드:{(매개변수): 반환값}
c: {
(): void,
};
}셋은 거의같지만 한가지 경우에서 다르다.
공변성과 반공변성
이에대해 알아봐야하는게 공변성과 반공변성이다.
공변성: A -> B 일때 T<A> -> T<B>반공변성: A -> B 일때 T<B> -> T<A>이변성: A -> B 일때 T<A> -> T<B> 도되고 T<B> -> T<A> 도 되는경우무공변성: A -> B 일때 T<A> -> T<B> 도 안되고 T<B> -> T<A> 도 안되는경우
기본적으로 타입스크립트는 공변성을 가지고 있지만, 함수의 매개변수는 반공변성(strict 가 활성화 되어 있지 않으면 이변성)을 갖고 있다.
이를 확인하기 위해서는 TS Config 메뉴에서 strictFunctionTypes 옵션이 체크✔️ 되어 있어야 한다.
strictFunctionTypes 옵션은 strict 옵션이 체크되어 있을때 자동으로 활성화 되긴한다.
만약 strict 와 strictFunctionTypes 모두 체크되어 있지 않다면 타입스크립트는 매개변수에 대해 이변성을 갖는다.
function a(x: string): number {
return 0;
}
type B = (x: string) => number | string;
let b: B = a;a 함수를 b 타입의 대입 할 수 있다.
a 함수와 b의 함수 두개의 차이는 반환값 의 차이 밖에 없다.
a는 number만 반환하고 b는 number와 string을 반환하는 차이 밖에 없다.
이 관계를 a -> b라고 표현 할수 있다.
반대의 상황에서는 그럼 b -> a 가 되는 상황일것 !
위에 1번 코드에서 반환값을 서로 바꿔주기만 해보면 어떻게 될까?
function a(x: string): number | string {
return 0;
}
type B = (x: string) => number;
let b: B = a;서로의 반환값만 a는 number 에서 -> number | string 으로,
type B는 반환값타입을 number | string 에서 -> number로 만 바꿔준 상황이다.
이때 b함수에서 B type을 함수a 에 대입하면 에러가 발생한다.

sting | number 를 number에 대입할 수 없다는 에러가 발생한다.
strict 옵션을 해제해도 여전히 에러가 발생한다.
공변성은 간단하게 A -> B 만 가능하다.
2번 코드에서는 B -> A 를 했더니 에러가 발생한다.
조금 간단하게 생각하면 타입스크립트는 A -> B 는 가능하지만 B -> A 는 불가능 하다.
즉,
typescript는 반환값에 대해서는 항상 공변성을 가진다고 볼수 있다.
매개변수는 반공변성을 가진다.
function a(x: sting | number): number {
return 0;
}
type B = (x: string) => number;
let b: B = a;아까 와는 다르게 반환값이 아닌 매개변수에 집중해야한다.
매개변수를 보게되면 string -> sting | number 이므로 쉽게 말해 B -> A 인 상황이라고 할 수 있다.
B의 타입을 가지고 잇는 b 함수에 a 값을 지정했는데 에러가 발생하지 않느다.
반대는 불가능하다.
function a(x: string): number {
return 0;
}
type B = (x: string | number) => number;
let b: B = a;
하지만 만약 strict 옵션을 해제하면 어떻게 될까? 반환값에 대해서는 해제를 하거나 안하거나 관계없이 에러가 발생했지만
매개변수에서 strictFunctionTypes 옵션을 해제하면 위에 4번코드에서 에러가 발생하지 않는다.
즉, 매개변수는 strictFunctionTypes 옵션일때는 반공변성 이지만 , 옵션을 해제하면 이변성, a -> b 이거나 b -> a여도 된다고 말해주고 있다.
처음 맨위에 객체 메서드 선언 세가지 방식 을 적어놨는데 메서드를 선언 할때도 타이핑 방법에 따라 변성이 정해진다.
이는 strict옵션이 활성화 되었다는 가정하에 말한다.
interface SayMethod {
say(a: string | number): string;
}
interface SayFunction {
say: (a: string | number) => string;
}
interface SayCall {
say: {
(a: stinrg | number): string,
};
}
const sayFunc = (a: string) => "hello";
const MyAddingMethod: SayMethod = {
say: sayFunc, //이변성
};
const MyAddingfunction: SayFunction = {
say: sayFunc, // 반공변성
};
const MyAddingfunction: SayCall = {
say: sayFunc, // 반공변성
};정리
- 타입스크립트는 기본적으로 공변성을 가지고 있고 함수의 매개변수는 반공변성을 갖고있다.
- 메서드(매개변수) : 반환값 은 이변성 을 가지고있다. (이해가 더 필요한 부분같다)
- 타입스크립트 반환값
- a를 number | sting b를 number 라고 생각해보자.
- a의 반환값을 b에 대입할 수 있다. a -> b ⭕️
- b의 반환값을 a에 대입할 수 없다. b -> a ❌