인프런 커뮤니티 질문&답변

raipendalk님의 프로필 이미지
raipendalk

작성한 질문수

[리뉴얼] 타입스크립트 올인원 : Part1. 기본 문법편

공변성과 반공변성

함수의 공변성과 반공변성에 대한 이유 설명

작성

·

641

6

질문은 아니지만 다른 분들도 쉽게 보시고 이해하실 수 있도록 해당 수업의 질문으로 남기도록 하겠습니다.
설명에 앞서 그냥 강사님께서 하신 말씀처럼 외우는게 제일 나을수도 있습니다. 그냥 저처럼 이해하지 못하면 못 넘어가는 사람들을 위해 남깁니다.


공변성과 반공변성에 대하여 매개변수와 리턴타입의 대입가능성에 대해 설명하고자 합니다. 강의에서 사용된 예제를 사용하겠습니다.

function a(x: string | number): number{
    return 0;
}
type B = (x: string) => number | string;
let b: B = a;

b("asdf") // B타입에 맞음 정상
b(123) //B타입에 맞지 않는 매개변수 이므로
       //typescript가 오류를 잡아줄 수 있음

먼저 매개변수에 대한 이야기입니다. 변수 b의 B 타입으로 정의되었습니다. 즉, 매개변수로는 x: string만 오도록 강제할 것입니다. 이는 b("asdf")로 사용했을 때 a("asdf")를 사용한 것과 같은 상황이니 문제가 없습니다. 하지만 반대면 어떨까요?

function a(x: string): number{
    return 0;
}
type B = (x: string | number) => number | string;
let b: B = a;

b("asdf") // 타입 B에 대해서도 정상
          //a함수가 실행될때도 정상
b(123); // B타입상으로만 보면 정상임
//그러나 실제로 a함수가 실행될 때 오류
//따라서 이런 상황이 발생하지 않도록
//타입스크립트는 타입B에 함수 a를 할당할 수 없도록 함

만약 이 상황이라면 변수 b는 B타입이기 때문에 매개변수로 number도 사용하능 합니다. 즉 타입상으로는 b(123)이 가능하다는 것이죠, 하지만 이 경우 a(123)이 실행되는 것과 같은데 a의 매개변수로는 string만 가능하기 때문에 문제가 발생합니다.

정리하자면 실제로 대입된 함수의 매개변수가 허용하지 않는 타입을 넣으면 안 되기 때문에, (대입되는 함수의 매개변수)는 (타입의 매개변수)와 같거나 더 넓어야합니다.


리턴값도 비슷한 원리입니다.

function a(x: string | number): number{
    return 0;
}
type B = (x: string) => number | string;
let b: B = a;
let result = b("asdf");
//result의 타입은 B타입에 의해 number|string 으로 추론됨
//실행결과 실제 타입은 number로 추론과 일치

타입스크립트 입장에서 b("asdf")의 예상 결과는 number | string 이겠죠. b는 타입 B 니까요. 그리고 실제로 b("asdf")는 실제로 a("asdf")를 실행하는 것과 같으니 실제로는 number가 리턴됩니다. 타입스크립트는 number | string를 예상하고 있고, 거기에 number가 온 것이니 문제가 없습니다.

반대 상황을 봅시다.

function a(x: string | number): number | string {
    return 0;
}
type B = (x: string) => number;
let b: B = a;
let result = b("asdf");
//타입 B만 보고 추론하면 result는 number임
//근데 실제 실행결과로 result는 string이 될 수도 있음

타입스크립트는 b("asdf")의 결과로 number 만을 기대할 것입니다. 근데 실제로 a("asdf")number | string 이에요. 그러면 원래는 b의 실행결과로는 number만 예상하고 있었는데 실제 결과로 number | string 이면 문제가 있겠죠?

정리하자면 실제로 대입된 함수가 리턴한 값이 타입의 리턴값에 포함되지 않으면 안 되기 때문에, (대입되는 함수의 리턴값)는 (타입의 리턴값)와 같거나 더 좁아야합니다.


위 모든 원칙은, 타입스크립트가 실제로 대입된 함수의 타입은 신경쓰지 않고, 현재 정의된 타입이 뭔지만 신경쓰기 위해서 존재한다고 생각하시면 이해가 편하실수도 있을 것 같네요...

타입스크립트의 타입검사기 입장에서 let b: B = a;이후에 b를 쓸 때, a에 대해서 신경1도 안쓰고 B에 대해서만 타입 검사를 해주려고 처음 대입할때 매개변수의 범위와 리턴값의 범위를 저렇게 제한했다고 보시면 된다는 뜻입니다.


답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

훌륭한 분석이십니다!!

0

오 덕분에 이해할 수 있었습니다! 감사합니다

raipendalk님의 프로필 이미지
raipendalk

작성한 질문수

질문하기