Web Front-end 공부/TypeScript
[TypeScript] 서브타입과 슈퍼타입
Hanachoi
2023. 2. 27. 10:48
# 타입트리 계층
- 타입스크립트에서 모든 타입은 계층구조에서 자리를 차지한다. 타입은 트리의 부모노드와 자식노드가 될 수 있다.
- 타입시스템에서 이러한 관계에 대해 상위노드를 슈퍼타입, 하위노드를 서브타입이라고 한다
- "Car is Vehicle" : 부모클래스가 Vehicle, 자식클래스가 Car일때, Car is Vehicle은 성립하는 문장이지만 그 반대는 안된다. 이 상속의 의미론은 타입스크립트의 타입계층에서도 적용된다.
타입스크립트 타입 호환성과 타입 계층 트리
타입스크립트의 타입 계층을 통해 타입 호환성을 알아봅시다. 1. 타입 호환성은 뭔가요? 타입 호환성 Documentation - Type Compatibility How type-checking works in TypeScript www.typescriptlang.org 타입스크립트에는
itchallenger.tistory.com
- 서브타입과 슈퍼타입의 관계를 코드로 보자
- 각각의 sub 1,2,3은 sup1,2,3에 들어갈 수 있지만 그 반대는 작동하지 않는다.
// sub 1 타입은 sup1타입의 서브타입이다.
// sup 1 타입은 sub1 타입의 슈퍼타입이다.
let sub1: 1 =1; // 리터럴 타입으로, sub1에는 꼭 숫자1이 들어와야함
let sup1: number = sub1;
sub1 = sup1 // error! Type 'number'is not assignnale to type '1'
//sub2타입은 sup2타입의 서브타입이다.
// sup2타입은sup2타입의 슈퍼타입이다.
// 배열도 object의 일종이다.
let sub2: number[] = [1];
let sup2 : object = sub2;
sub2 = sup2 // error!
//sub3타입은 sup3타입의 서브타입이다.
//sup3 타입은 sup3타입의 슈퍼타입이다
let sub3: [number, number] = [1,2];
let sup3: number[]= sub3;
sub3 = sup3 //error!
- 특별한 케이스들도 존재한다.
- any타입은 어떠한 타입도 허용해준다. 타입을 엄격하게 검사하고 관리하는 타입스크립트에서는 딱히 좋은 코드라고 할 수없다.
- never타입은 어떤 것도 할 수 없다. 하지만 never는 모든 타입이 서브타입으로 가지고 있는 형태이다.
// sub4 타입은 sup4타입의 서브타입이다
let sub4: number = 1;
let sup4: any = sub4;
sub4=sup4;
//sub5타입은 sup5타입의 서브타입이다.
let sub5: never = 0 as never;
let sup5: number = sub5;
sub5 = sup5; // error! Type'number'is not assignable to type 'never'
class SubAnimal {}
class SubDog extends SubAnimal {
eat(){}
}
//sub6타입은 sup6타입의 서브타입이다
//sup6 타입은 sub6타입의 슈퍼타입이다
let sub6: SubDog = new SubDog();
let sup6: SubAnimal = sub6;
sub6 = sup6 // error! Property 'eat'is missing in type 'SubAnimal' but requried in type 'SubDog'
- 1. 같거나 서브타입인 경우, 할당이 가능하다 => 공변성이라고 부른다.
// primitive type
//sup7의 타입은 string과 number의 union타입이고 sub7은 string이니 할당 가능
let sub7: string = '' ;
let sup7: string | number = sub7;
//object : 각각의 프로퍼티가 대응하는 프로퍼티와 같거나 서브타입이어야 한다.
let sub8: {a:string; b: number} = {a:'', b:1};
let sup8: {a:string | number; b: number} = sub8
//array도 object와 마찬가지로 작동
let sub9: Array<{a:string; b:number}> = [{a:'', b:1}];
let sup9: Array<{a: string | number; b:number}> = sub8
- 2. 함수의 매개변수 타입만 같거나 슈퍼타입인 경우, 할당이 가능하다 => 반병
class Person {}
class Developer extends Person{
coding(){}
}
class StartupDeveloper extends Developer{
bunring(){}
}
function tellme(f: (d: developer)=>Developer){} // developer를 받아서 Developer를 반환하는 함수
//Developer => Developer 에다가 Developer => Developer를 할당하는 경우
//위의 tellme함수와 정확하게 일치하기 때문에 할당 가능
tellme(function dToD(d:developer): Developer) {
return new Developer();
}
//Developer => Developer 에다가 Person => Developer를 할당하는 경우
//할당되는 매개변수가 슈퍼타입인 Person이 들어옴
//매개변수 d가 원래 함수 tellme의 매개변수와 일치하거나, tellme의 슈퍼타입인 경우 할당이 가능
tellme(function pToD(d: Person):Developer){
return new Developer();
}
// Developer => Developer에다가 StartupDeveloper => Developer를 할당한 경우
//원래는 안되는 경우인데 할당이 되어버림
tellme(function sToD(d: StartupDeveloper):Developer){
return new Developer();
}
- strictFunctionTypes 옵션을 키면, 함수의 매개변수 타입만 같거나 슈퍼타입인 경우가 아니면 에러를 통해 경고한다.
- any타입은 입력이 마음대로 가능, 함수구현이 자유롭게 가능하다
- 하지만 항상 자유로운게 좋은 것만은 아니다
function fany(a:any): number | string | void{
a.toString();
if(typeof a ==='number'){
return a*38
}else if(typeof a ==='string'){
return `Hello ${a}`;
}
}
console.log(fany(10)) // 380
console.log(fany('Mark')) // Hello Mark
console.log(fany(true)) // undefined
- unknown타입 : 입력이 마음대로 가능하지만 함수구현은 문제가 없도록 도와준다.
- any타입처럼 모든 값을 허용하지만 할당된 값이 어떤 타입인지 모르기 때문에 함부로 연산할 수 없다. 프로퍼티 또는 연산을 하는 경우에 컴파일러가 미리 체크해 문제되는 코드를 발견하게 해준다.