공변성(Covariance)과 반공변성(Contravariance)은 Dog가 Animal의 서브타입일 때, 그 타입을 감싼 타입(배열, 함수)의 서브타입 관계가 같은 방향으로 유지되는지(공변) 아니면 뒤집히는지(반공변)를 말한다.
// 반환 타입은 공변: Dog를 돌려주는 함수는 Animal 팩토리로 쓸 수 있다
let makeAnimal: () => Animal = (): Dog => new Dog()
// 매개변수는 반공변: Animal을 처리하는 함수는 Dog 핸들러 자리에 들어갈 수 있다
let handleDog: (d: Dog) => void = (a: Animal) => {}
let handleAnimal: (a: Animal) => void = (d: Dog) => {} // ❌ strictFunctionTypes- 직관: 더 많은 것을 받아들이고(매개변수), 더 구체적인 것을 돌려주는(반환) 함수가 더 안전한 대체품이다. 리스코프 치환 원칙(리스코프 치환 원칙)을 함수 타입에 적용한 것이다
- 콜백·이벤트 핸들러를 넘길 때 이 규칙이 그대로 나온다.
Event를 처리하는 리스너는MouseEvent리스너 자리에 넣을 수 있지만, 반대는 안 된다 strictFunctionTypes(strict에 포함)를 켜야 함수 프로퍼티의 매개변수가 반공변으로 검사된다. 메서드 문법(handle(x: T): void)으로 선언한 것은 여전히 이변적(Bivariant)으로 느슨하게 검사된다- 배열은 공변이다(
Dog[]를Animal[]에 넣을 수 있다). 사실 이건 불건전(unsound)하다.Animal[]로 받은 쪽이Cat을 push하면 원래Dog[]가 오염된다. TS가 편의를 위해 허용한 구멍이고,readonly Animal[]로 받으면 이 문제가 사라진다
기초는 구조적 타이핑, 함수 타입 표기는 함수 오버로드와 시그니처.