
공변성(Covariance)
A가 B의 서브타입일 때, T<A>는 T<B>이면 타입 T는 공변이다.

반환 타입이 공변적이라는 것은 반환 타입이 서브타입 관계를 유지하는 것을 말합니다.
아래 예제에서 Cat은 Animal의 서브 타입니다. 그리고 Cat[]역시 Animal[]의 서브타입입니다. 서브 타입의 관계가 배열에서도 똑같이 유지됐음으로 공변적이라 할 수 있습니다.
type IsSubtypeOf<S, P> = S extends P ? true : false
class Animal {
name: string
constructor(name: string) {
this.name = name
}
}
class Cat extends Animal {
meow() {
console.log('Meow')
}
}
const cat = new Cat('야옹이')
const animal: Animal = cat
// true
type TypeX = IsSubtypeOf<Cat, Animal>
const catList:Cat[] = [cat];
const animalList:Animal[] = catList
// true
type TypeList = IsSubtypeOf<Cat[], Animal[]>
반변성(Contravariance)
A가B의 서브타입일 때 , T<B>는 T<A>이면 T는 반공변이다.

.함수의 매개변수 타입에 대해 반대로 적용됩니다. 앞선 예제에서 catList를 animalList에 할당 할 수 있었던 것과 달리 함수의 매개 변수에 대해서는 catHandler를 animalHandler에 할당할 수 없습니다. 반대로 catHander에는 handleAnimal을 할당할 수 있습니다. Cat과 Animal의 서브타입 관계가 역전됐음을 볼 수 있는데 이를 반변적이라 합니다. 예제의 상황을 생각해보면 catHandler에 HandleAnimal이 할당된 경우에는 매개변수로 cat인스턴스가 들어오고 cat 인스턴스는 Animal을 상속했기 때문에 HandleAnimal의 모든 기능을 수행할 수 있습니다.
반면 animalHandler에 catHandler가 할당된다면 매개변수로 Animal을 상속한 Bird 인스턴스가 들어올 수 있고 bird 인스턴스에는 meow 메소드가 없음으로 런타임 오류가 날 것입니다.
Cat가 Animal의 서브 타입이었던 관계가 AnimalHandler가 CatHandler의 서브 타입이 됨으로써 관계가 함수의 매개변수에 대해서는 서브셋의 관계가 역전됨을 알 수 있습니다
type Handler<T> = (arg: T) => void
type AnimalHandler = Handler<Animal>
type CatHandler = Handler<Cat>
// true Cat은 Animal의 서브타입이다.
type Type = IsSubtypeOf<Cat, Animal>
// false CatHandler은 AnimalHandler의 서브타입이 아니다.
type TypeHandler = IsSubtypeOf<CatHandler, AnimalHandler>
function handleAnimal(animal: Animal) {
console.log(animal.name)
}
function handleCat(cat: Cat) {
cat.meow()
}
let animalHandler: AnimalHandler
let catHandler: CatHandler
catHandler = handleCat
// ✅더 구체화된 Cat인스턴스는 더 일반적인 Animal의 모든 기능을 수행할 수 있습니다.
catHandler = handleAnimal
animalHandler = handleAnimal
// 🚫Error 더 일반적인 Animal 인스턴스는 더 구체적인 Cat 인스턴의 메소드가 없습니다.
animalHandler = catHandler

유니온 타입 예제
string|number 인 타입 TypeA와 string타입인 TypeB가 있을 때 타입 B는 타입 A의 서브타입니다.
B ⊂ A의 관계는 함수의 매개변수에서 역전되어 F<A> ⊂ F<B> 가 됩니다.
// B ⊂ A
type TypeA = string | number
type TypeB = string
type Func<T> = (arg: T) => string
// true TypeB는 TypeA의 서브타입이다
type UnionType = IsSubtypeOf<TypeB, TypeA>
// false Func<TypeB>는 Func<TypeA>의 서브타입이 아니다.
type FuncType = IsSubtypeOf<Func<TypeB>, Func<TypeA>>
let dataA: TypeA = 1
let dataB: TypeB = '1'
// 🚫 B ⊂ A 임으로 B에 A를 할당할 수 없다.
dataB = dataA
// ✅ B ⊂ A임으로 B를 A에 할당 가능. A가 더 큰 타입이다.
dataA = dataB
let functionA: Func<TypeA> = param => {
return param.toString()
}
let functionB: Func<TypeB> = param => {
return param.toString()
}
// 🚫 매개변수에 대해서는 B ⊂ A의 관계가 A ⊂ B가 된다.
// functionB는 매개변수가 string인 경우를 처리할 수 없다.
functionA = functionB
// ✅ functionA는 매개변수가 string으로 오는 경우에 대해서만 처리하게 된다.
functionB = functionA
매개변수 예제
type SingleArgHandler = (x:any) => void;
type DoubleArgHandler = (x:any, y:any) => void;
// true, SingleArgHandler은 DoubleArgHandler의 서브타입이다.
type Argument = IsSubtypeOf<SingleArgHandler,DoubleArgHandler>
let singleHandler: SingleArgHandler = (x) => {
console.log(x)
}
let doubleHandler: DoubleArgHandler = (x,y) => {
console.log(x, y)
}
// ✅ 두 개의 매개변수를 입력받더라도 하나(x)에 대해서만 처리한다.
doubleHandler = singleHandler;
// 🚫 y가 전달되지 않기 때문에 런타임 에러가 발생한다.
// Target signature provides too few arguments. Expected 2 or more, but got 1.
singleHandler = doubleHandler
참고
[번역] 타입스크립트의 공변성과 반공변성
원문: https://dmitripavlutin.com/typescript-covariance-contravariance
medium.com
'typescript' 카테고리의 다른 글
any 타입이 유용한 경우 (1) | 2024.07.13 |
---|---|
Generic을 활용한 복잡한 구조의 타입 추론하기 (0) | 2024.07.05 |
실무자를 위한 현장 밀착 Typescript Best Practice (0) | 2024.06.21 |

공변성(Covariance)
A가 B의 서브타입일 때, T<A>는 T<B>이면 타입 T는 공변이다.

반환 타입이 공변적이라는 것은 반환 타입이 서브타입 관계를 유지하는 것을 말합니다.
아래 예제에서 Cat은 Animal의 서브 타입니다. 그리고 Cat[]역시 Animal[]의 서브타입입니다. 서브 타입의 관계가 배열에서도 똑같이 유지됐음으로 공변적이라 할 수 있습니다.
type IsSubtypeOf<S, P> = S extends P ? true : false
class Animal {
name: string
constructor(name: string) {
this.name = name
}
}
class Cat extends Animal {
meow() {
console.log('Meow')
}
}
const cat = new Cat('야옹이')
const animal: Animal = cat
// true
type TypeX = IsSubtypeOf<Cat, Animal>
const catList:Cat[] = [cat];
const animalList:Animal[] = catList
// true
type TypeList = IsSubtypeOf<Cat[], Animal[]>
반변성(Contravariance)
A가B의 서브타입일 때 , T<B>는 T<A>이면 T는 반공변이다.

.함수의 매개변수 타입에 대해 반대로 적용됩니다. 앞선 예제에서 catList를 animalList에 할당 할 수 있었던 것과 달리 함수의 매개 변수에 대해서는 catHandler를 animalHandler에 할당할 수 없습니다. 반대로 catHander에는 handleAnimal을 할당할 수 있습니다. Cat과 Animal의 서브타입 관계가 역전됐음을 볼 수 있는데 이를 반변적이라 합니다. 예제의 상황을 생각해보면 catHandler에 HandleAnimal이 할당된 경우에는 매개변수로 cat인스턴스가 들어오고 cat 인스턴스는 Animal을 상속했기 때문에 HandleAnimal의 모든 기능을 수행할 수 있습니다.
반면 animalHandler에 catHandler가 할당된다면 매개변수로 Animal을 상속한 Bird 인스턴스가 들어올 수 있고 bird 인스턴스에는 meow 메소드가 없음으로 런타임 오류가 날 것입니다.
Cat가 Animal의 서브 타입이었던 관계가 AnimalHandler가 CatHandler의 서브 타입이 됨으로써 관계가 함수의 매개변수에 대해서는 서브셋의 관계가 역전됨을 알 수 있습니다
type Handler<T> = (arg: T) => void
type AnimalHandler = Handler<Animal>
type CatHandler = Handler<Cat>
// true Cat은 Animal의 서브타입이다.
type Type = IsSubtypeOf<Cat, Animal>
// false CatHandler은 AnimalHandler의 서브타입이 아니다.
type TypeHandler = IsSubtypeOf<CatHandler, AnimalHandler>
function handleAnimal(animal: Animal) {
console.log(animal.name)
}
function handleCat(cat: Cat) {
cat.meow()
}
let animalHandler: AnimalHandler
let catHandler: CatHandler
catHandler = handleCat
// ✅더 구체화된 Cat인스턴스는 더 일반적인 Animal의 모든 기능을 수행할 수 있습니다.
catHandler = handleAnimal
animalHandler = handleAnimal
// 🚫Error 더 일반적인 Animal 인스턴스는 더 구체적인 Cat 인스턴의 메소드가 없습니다.
animalHandler = catHandler

유니온 타입 예제
string|number 인 타입 TypeA와 string타입인 TypeB가 있을 때 타입 B는 타입 A의 서브타입니다.
B ⊂ A의 관계는 함수의 매개변수에서 역전되어 F<A> ⊂ F<B> 가 됩니다.
// B ⊂ A
type TypeA = string | number
type TypeB = string
type Func<T> = (arg: T) => string
// true TypeB는 TypeA의 서브타입이다
type UnionType = IsSubtypeOf<TypeB, TypeA>
// false Func<TypeB>는 Func<TypeA>의 서브타입이 아니다.
type FuncType = IsSubtypeOf<Func<TypeB>, Func<TypeA>>
let dataA: TypeA = 1
let dataB: TypeB = '1'
// 🚫 B ⊂ A 임으로 B에 A를 할당할 수 없다.
dataB = dataA
// ✅ B ⊂ A임으로 B를 A에 할당 가능. A가 더 큰 타입이다.
dataA = dataB
let functionA: Func<TypeA> = param => {
return param.toString()
}
let functionB: Func<TypeB> = param => {
return param.toString()
}
// 🚫 매개변수에 대해서는 B ⊂ A의 관계가 A ⊂ B가 된다.
// functionB는 매개변수가 string인 경우를 처리할 수 없다.
functionA = functionB
// ✅ functionA는 매개변수가 string으로 오는 경우에 대해서만 처리하게 된다.
functionB = functionA
매개변수 예제
type SingleArgHandler = (x:any) => void;
type DoubleArgHandler = (x:any, y:any) => void;
// true, SingleArgHandler은 DoubleArgHandler의 서브타입이다.
type Argument = IsSubtypeOf<SingleArgHandler,DoubleArgHandler>
let singleHandler: SingleArgHandler = (x) => {
console.log(x)
}
let doubleHandler: DoubleArgHandler = (x,y) => {
console.log(x, y)
}
// ✅ 두 개의 매개변수를 입력받더라도 하나(x)에 대해서만 처리한다.
doubleHandler = singleHandler;
// 🚫 y가 전달되지 않기 때문에 런타임 에러가 발생한다.
// Target signature provides too few arguments. Expected 2 or more, but got 1.
singleHandler = doubleHandler
참고
[번역] 타입스크립트의 공변성과 반공변성
원문: https://dmitripavlutin.com/typescript-covariance-contravariance
medium.com
'typescript' 카테고리의 다른 글
any 타입이 유용한 경우 (1) | 2024.07.13 |
---|---|
Generic을 활용한 복잡한 구조의 타입 추론하기 (0) | 2024.07.05 |
실무자를 위한 현장 밀착 Typescript Best Practice (0) | 2024.06.21 |