인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

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

민수연님의 프로필 이미지

작성한 질문수

한 입 크기로 잘라먹는 타입스크립트(TypeScript)

함수 타입의 호환성

함수타입의 호환성에 대해 질문드립니다

해결된 질문

작성

·

47

0

안녕하세요, 함수타입의 호환성에 대해 궁금한게 생겨 질문드립니다.

type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: "기린",
  color: "yellow",
};

let dog: Dog = {
  name: "돌돌이",
  color: "brown",
  breed: "진도",
};

animal = dog; // ✅ OK
dog = animal; // ❌ NO

이전 강의인 '타입스크립트 이해하기>객체 타입의 호환성' 에서는 위 코드에서 animal=dog는 가능하지만 dog=animal은 안된다고 하셨습니다. dog가 animal의 서브타입이기에 '수퍼를 서브에 넣을 순 없다' 라는 개념까지는 이해했습니다.

 

그런데 오늘 강의에서는 비슷한 코드인데 dogFunc을 animalFunc에 넣을 수 없다고 한 것이 이해되지 않습니다. dog가 animal의 서브인건 여전한데 왜 이 경우엔 안되는건가요?

type Animal = {
  name: string;
};

type Dog = {
  name: string;
  color: string;
};

let animalFunc = (animal: Animal) => {
  console.log(animal.name);
};

let dogFunc = (dog: Dog) => {
  console.log(dog.name);
  console.log(dog.color);
};

animalFunc = dogFunc; // ❌
dogFunc = animalFunc; // ✅

 

답변 2

0

이정환 Winterlood님의 프로필 이미지
이정환 Winterlood
지식공유자

안녕하세요 이정환입니다.

타입스크립트에서 함수 타입의 호환성을 평가하면서 매개변수 간의 호환을 판단할 때에는 아주 특별하게 다운 캐스팅이 허용되고, 업 캐스팅은 허용되지 않습니다.

이렇게 되는 이유는 A, B 두 함수가 있을 때 "A 매개변수" 타입이 "B 매개변수" 타입의 슈퍼타입이라면 A 함수는 B 함수의 서브 타입이 되기 때문입니다. 반대로 "A 매개변수" 타입이 "B 매개변수" 타입의 서브 타입이라면 A 함수는 B 함수의 슈퍼타입이 됩니다.

쉽게 말하자면 두 함수 타입간의 관계는 매개변수가 서브타입인 쪽이 슈퍼타입이 된다고 이해하시면 됩니다.

그 이유에 대해서는 코드와 함께 설명드리도록 하겠습니다. 아래의 두 코드를

type Animal = {
  name: string;
};

type Dog = {
  name: string;
  color: string;
};

let animalFunc = (animal: Animal) => {
  console.log(animal.name);
};

let dogFunc = (dog: Dog) => {
  console.log(dog.name);
  console.log(dog.color);
};

animalFunc = dogFunc; // ❌  ①
dogFunc = animalFunc; // ✅

위 코드를 살펴보면 Animal 타입의 매개변수를 받는 animalFunc 함수의 타입은 (value : Animal) => void 가 됩니다. 반면 Dog 타입의 매개변수를 받는 dogFunc 함수의 타입은 (value: Dog) => void가 되겠죠?

이때 ①에서 (value : Animal) => void 타입의 변수 animalFunc에 (value: Dog) => void 타입의 값을 할당하려고 하면 오류가 발생하는데요 그 이유에 대해 천천히 살펴보겠습니다.

우선 이 할당식은 dogFunc 함수 타입의 값을, animalFunc 함수 타입의 값으로 취급하는 것입니다. dogFunc 함수 타입의 값을 animalFunc 함수 타입의 변수에 할당하고 있으니까요 위 내용을 코드로 표현하면 다음과 같습니다.

type AnimalFunc = (value: Animal) => void;

let dogFunc: AnimalFunc = (dog: Dog) => { // 타입 오류 발생
  console.log(dog.name);
  console.log(dog.color);
};

그러나 이는 오류로 평가됩니다. 그 이유는 Animal 타입의 매개변수에는 color 등의 프로퍼티가 없는데, 함수 내부에서는 Dog 타입의 매개변수가 제공될 것을 예상하고 color 등의 Dog 타입에만 존재하는 프로퍼티를 사용하고 있기 때문입니다.

이처럼 타입스크립트에서 함수 타입간의 호환성을 평가할 때에는 매개변수의 타입은 오히려 다운 캐스팅이 허용되는 방향으로 예외적으로 동작한다고 이해하시면 됩니다. 자세한 이유에 대해서는 공변/반공변 개념에 대해 추가적으로 살펴보시면 도움이 될 듯 합니다.

혹시 이해가 잘 안되신다면 답글 주세요!

민수연님의 프로필 이미지
민수연
질문자

이해 됐습니다! 감사합니다 :-)

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. 민수연님.

함수 타입의 호환성에 대해 질문해 주셨군요. 타입스크립트에서의 함수 타입 호환성은 일반 객체 타입의 호환성과 달리, 매개변수의 관계반환 값까지도 고려됩니다. 이를 이해하기 위해 함수의 매개변수와 반환값이 어떻게 호환되는지를 살펴보겠습니다.

함수 타입의 호환성

  1. 매개변수의 설정:

    • 함수의 매개변수는 공변(covariant) 관계가 아닌 반공변(contravariant) 관계입니다. 즉, 덜 구체적인 타입(슈퍼타입)은 더 구체적인 타입(서브타입)을 허용하지 않습니다.
    • 즉, dogFunc의 매개변수는 Dog 타입으로 namecolor가 필요합니다. 이 함수에 Animal만 받는 animalFunc을 대체할 수 없습니다, 왜냐하면 dogFunc는 추가적인 color 프로퍼티가 필요하기 때문입니다.
  2. 반환값의 설정:

    • 함수의 반환값은 공변(covariant) 관계로, 더 구체적인 타입으로 반환값이 설정될 수 있습니다.

따라서, 주어진 코드에서:
- animalFunc = dogFunc; 는 불가능합니다. animalFuncname만 처리할 수 있지만 dogFunccolor도 처리하려고 하기 때문에 함수의 매개변수 조건을 충족하지 못해 대입이 불가능합니다.
- dogFunc = animalFunc; 는 가능합니다. dogFunc의 요구사항보다 적은 요구사항을 가지기 때문에 문제되지 않습니다.

함수형 프로그래밍에서 자주 말하는 런타임 오류를 방지하기 위해 타입스크립트는 이러한 호환성을 철저히 검사합니다.

관련 질문 URL:
- 함수 타입의 호환성에 대해서 궁금한 사항이 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.