본문 바로가기
4주차

변성 가족 알아보기 (공변성, 반공변성, 이변성, 불변성)

by mnbvcxz05 2025. 5. 13.

안녕하세요 SOPT 36기 웹 YB 김예지입니다. 

 

오늘은 공변성, 반공변성에 대해 알아보겠습니다.. 공변성반공변성이변성불변성 발음도 어렵고 이름도 어렵게 생겼습니다. 흑흑. 

이 함수 타입을 다른 곳에 넣어도 될지.. 이 객체를 이 타입에 할당할 수 있을지... 이런 고민을 해보셨다면 한 번쯤 들어보셨을 거 같아요.

 

변성

공통으로 들어가있는 변성. 이란..

어떤 타입이 다른 타입과 할당 관계를 가질 수 있는지 판단하는 기준입니다. 

 

타입 A가 있고 타입 B가 있을 때,

- A를 -> B에 할당 가능한지.

- B를 -> A에 할당가능한지..

 

이런 방향성을 기준으로 공변/반공변/이변/불변이 나뉘게 됩니다. 

 

 

공변성 Convariance 

자식타입을 부모 타입에 대입할 수 있다는 개념입니다.

다시 말해, 타입이 구체적일 때 상위 타입 자리에 들어갈 수 있는 것을 의미합니다. 

 

예를 들어 아래와 같은 코드가 있다고 할 때, 

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

const dog: Dog = { name: '구름', breed: '말티즈' };

const animal: Animal = dog; // Dog → Animal (공변성)

 

DogAnimal을 포함하고 있기 때문에, 더 많은 정보를 가진 타입적은 정보를 가진 타입 자리에 들어갈 수 있어요. 이를 공변성이라고 합니다. 

 

 

반공변성 Contravariance

부모 타입을 자식 타입에 대입할 수 있다는 개념입니다. 

다시 말해, 어떤 타입이 더 일반적일 때, 하위 타입 자리에 들어갈 수 있다는 뜻입니다.

 

주로 함수 매개변수에서 많이 사용되는데요, 아래 코드를 보시면.. 

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

const useAnimal = (a: Animal) => {
  console.log(a.name);
};

const useDog: (d: Dog) => void = useAnimal; // 가능

 

useAnimalAnimal을 처리하는 함수인데, 이 함수를 Dog을 처리하는 함수 자리에 넣고 있습니다. 

Dog는 Animal보다 더 구체적인 타입이지만, Animal을 처리할 수 있다면 당연히 Dog도 처리할 수 있어요. 

 

타입스크립트에서는 함수 매개변수에서 반공변을 허용합니다. 

 

 

 

이변성 Bivariance 

타입스크립트는 함수 매개변수에 대해서 엄격한 반공변이 아니라 이변성을 적용합니다. 다시 말하면, 공변성과 반공변성을 둘 다 허용하는 것을 의미합니다.

 

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

type CallbackDog = (dog: Dog) => void;

const fn1: CallbackDog = (animal: Animal) => {
  console.log(animal.name);
}; // 이변성

 

엄격하게 말하면 animaldog가 아닙니다. 실제로는 타입 오류 가능성이 매우 존재하지만 실용성을 위해서 의도적으로 허용하는 것입니다.

 

tsconfig.json에서 strictFunctionTypes: true로 설정하면 이변성은 막히고 정확한 반공변성만 허용됩니다.

{
  "compilerOptions": {
    "strictFunctionTypes": true
  }
}

 

불변성 Invariance 

완전히 동일한 타입만 허용하는 것을 의미합니다. 

주로 배열이나 제네릭 타입에서 자주 나타납니다. 

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

const dogs: Dog[] = [{ name: '구름', breed: '말티즈' }];
const animals: Animal[] = dogs; // ❌ 오류 발생

Dog[]Animal[]처럼 다루면 타입 안전성이 깨질 수 있기 때문에 타입스크립트가 금지하는 것입니다. 

배열과 제네릭은 불변이기 때문에 타입이 정확하게 일치해야만 대입이 가능합니다. 

 

 

마무리

꽤나 생소한 단어이지만 결국 타입 간에 어떠한 방향으로 대입이 가능한가? 를 나타내는 개념인 거 같습니다. 

안정적이고 오류 없는 최고 타입스크립트를 위하여~....