타입 시스템에서는 Type variance(타입가변성)이라는 개념이 있습니다. 타입과 서브타입의 관계를 서술한 것들인데 언어마다 각기 다른방식으로 동작합니다. 함수형언어의 경우는 타입가변성을 조절 할 수도 있습니다.
Type variance에는 무엇이 있을까
이렇게 세가지 타입이 있습니다.
class Animal {}
class People extends Animal {}
class Hou extends People {}
A가 B의 서브타입일 경우 T<A>
도 T<B>
의 서브타입일 경우를 말합니다
리스코프 치환 원칙과 관련이 있습니다.
function method(People p) {}
method(animal) => error
method(people) => ok
method(hou) => ok
A가 B의 서브타입일 경우 T<B>
가 T<A>
의 서브타입일 경우를 말합니다
function method(People p) {}
method(animal) => ok
method(people) => ok
method(hou) => error
다른 타입을 허용하지 않음
function method(People p) {}
method(animal) => error
method(people) => ok
method(hou) => error
아무타입이나 허용함
function method(People p) {}
method(animal) => ok
method(people) => ok
method(hou) => ok
class Animal {
name: string
constructor() {
this.name = 'animal'
}
}
class People extends Animal {
constructor() {
super()
this.name = 'people'
}
coding() {}
}
class Hou extends People {
constructor() {
super()
this.name = 'hou'
}
queck(){}
}
let method = (a: People): People => a
let animal = new Animal()
let people = new People()
let hou = new Hou()
method(animal)
method(people)
method(hou)
//A<T>
let animals: Animal[] = new Array(5);
let peoples: People[] = new Array(5);
let hous: Hou[] = new Array(5);
animals[0] = hou
animals[1] = people
peoples[0] = animal //error
peoples[1] = hou
let coding = (a: People): People => { a.coding(); return a }
coding(animal[0])
coding(animal[0])
coding(animal) //error
coding(peoples[0])
coding(peoples[1])
//메서드 인수의 반공변성
let exMethod1: (x: Animal) => void
let exMethod2: (x: People) => void
let exMethod3: (x: Hou) => void
exMethod1 = exMethod2 // Error with --strictFunctionTypes
exMethod2 = exMethod1 // ok
exMethod2 = exMethod3 // Error with --strictFunctionTypes
exMethod3 = exMethod2 // ok
animal = people //ok
people = animal // error
people = hou // ok
hou = people // error
interface Comparer2<T> {
compare(a: T, b: T): number;
}
declare let animalComparer2: Comparer2<Animal>;
declare let dogComparer2: Comparer2<People>;
animalComparer2 = dogComparer2;
dogComparer2 = animalComparer2;
//메서드 인수와 리턴타입의 공변과 반공변성
declare let AnimalToAnimal: (a: Animal) => Animal;
declare let PeopleToAnimal: (a: People) => Animal;
declare let AnimalToPeople: (a: Animal) => People;
declare let PeopleToPeople: (a: People) => People;
AnimalToAnimal = PeopleToAnimal //err with --strictFunctionTypes
AnimalToAnimal = AnimalToPeople
AnimalToAnimal = PeopleToPeople //err with --strictFunctionTypes
PeopleToPeople = AnimalToAnimal //err
PeopleToPeople = PeopleToAnimal //err
PeopleToPeople = AnimalToPeople
//------------------------------
//하위형에서 메서드 인수의 반공변성
//하위형에서 반환형의 공변성
let f1: (x: Animal) => void;
let f2: (x: Dog) => void;
let f3: (x: Cat) => void;
f1 = f2; // Error with --strictFunctionTypes
f2 = f1; // Ok
f2 = f3; // Error
let animalArr: Animal[] = [animal];
let dogArr: Dog[] = [dog];
//----------------------------------
interface Comparer<T> {
compare: (a: T, b: T) => number;
}
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer;
dogComparer = animalComparer;
interface Comparer2<T> {
compare(a: T, b: T): number;
}
declare let animalComparer2: Comparer2<Animal>;
declare let dogComparer2: Comparer2<Dog>;
animalComparer2 = dogComparer2;
dogComparer2 = animalComparer2;
declare let aa: (a: Animal) => Animal;
declare let da: (a: Dog) => Animal;
declare let ad: (a: Animal) => Dog;
declare let dd: (a: Dog) => Dog;
aa = da
aa = ad
aa = dd
dd = aa
dd = da
dd = ad
/**
* Obviously Bad : Contravariance
* Animal <= Cat
* Animal[] >= Cat[]
*/
dogArr = animalArr; // Okay if contravariant
dogArr[0].wal(); // Allowed but BANG 🔫 at runtime
/** Type Hierarchy */
interface Point2D { x: number; y: number; }
interface Point3D { x: number; y: number; z: number; }
/** Two sample functions */
let iMakePoint2D = (): Point2D => ({ x: 0, y: 0 });
let iMakePoint3D = (): Point3D => ({ x: 0, y: 0, z: 0 });
/** Assignment */
iMakePoint2D = iMakePoint3D; // Okay
iMakePoint3D = iMakePoint2D; // ERROR: Point2D is not assignable to Point3D
var f: (x: { x: number; }) => void;
var g: (x: { x: number; y: number; }) => void;
g = f; // fair
f = g; // no error, but it should be
let ex1 = (e: Animal): Animal => e;
let ex2 = (e: Dog): Animal => new Animal();
let ex3 = (e: Animal): Dog => new Dog();
let ex4 = (e: Dog): Dog => e;
ex1(animal)
ex2(ex1(animal))
ex3(ex1(animal))
ex1(ex2(dog))
ex4(ex3(dog))
ex1(ex3(dog))