9. 타입 호환(Type Compatibility)
1. 타입 호환이란?
- 타입 호환이란 타입스크립트 코드에서 특정 타입이 다른 타입에 잘 맞는지를 의미한다.
- 예를 들면 다음과 같은 코드를 의미한다.
interface Ironman {
name: string;
}
class Avengers {
name: string;
}
let i: Ironman;
i = new Avengers();
- C#이나 Java였다면 위 코드에서 에러가 날 것이다.
- 왜냐하면
Avengers
클래스가 명시적으로 Ironman
인터페이스를 상속받아 구현하지 않았기 때문이다.
- 하지만 위와 같은 코드가 타입스크립트에서 정상적으로 동작하는 이유는 자바스크립트의 작동 방식과 관련이 있다.
- 기본적으로 자바스크립트는 객체 리터럴이나 익명 함수 등을 사용하기 때문에 명시적으로 타입을 지정하는 것보다는 코드의 구조 관점에서 타입을 지정하는 것이 더 잘 어울린다.
2. 구조적 타이핑 예시
- 구조적 타이핑이란 코드 구조 관점에서 타입이 서로 호환되는지의 여부를 판단하는 것이다.
interface Avengers {
name: string;
}
let hero: Avengers;
// 타입스크립트가 추론한 y의 타입은 { name: string; location: string; }이다.
let capt = { name: "Captain", location: "Pangyo" };
hero = capt;
- 위 코드에서
capt
가 hero
타입에 호환될 수 있는 이유는 capt
의 속성 중에 name
이 있기 때문이다.
Avengers
인터페이스에서 name
속성을 갖고 있기 때문에 capt
는 Avengers
타입에 호환될 수 있다.
interface Avengers {
name: string;
}
function assemble(a: Avengers) {
console.log("어벤져스 모여라", a.name);
}
let capt = { name: "Captain", location: "Pangyo" };
assemble(capt);
capt
변수에 이미 name
속성뿐만 아니라 location
속성도 있기 때문에 assemble
함수의 호출 인자로 넘길 수 있다.
3. Soundness란?
- 타입스크립트는 컴파일 시점에 타입을 추론할 수 없는 특정 타입에 대해서 일단 안전하다고 보는 특성이 있다.
- 이런 것을 "들리지 않는다(It is said to not be sound)"라고 표현한다.
4. 이넘 타입 호환 주의 사항
- 이넘 타입은
number
타입과 호환되지만 이넘 타입끼리는 호환되지 않는다.
enum Status {
Ready,
Waiting,
}
enum Color {
Red,
Blue,
Green,
}
let status = Status.Ready;
status = Color.Green; // Error
5. 클래스 타입 호환 주의 사항
- 클래스 타입은 클래스 타입끼리 비교할 때 스태틱 멤버와 생성자를 제외하고 속성(멤버 변수)만 비교한다.
class Hulk {
handSize: number;
constructor(name: string, numHand: number) {}
}
class Captain {
handSize: number;
constructor(numHand: number) {}
}
let a: Hulk = new Hulk("Hulk", 2);
let s: Captain = new Captain(2);
a = s;
s = a;
6. Generics
- 제네릭은 제네릭 타입 간의 호환 여부를 판단할 때 타입 인자
<T>
가 속성에 할당되었는지를 기준으로 한다.
interface Empty<T> {}
let x: Empty<number> = {};
let y: Empty<string> = {};
x = y;
- 위 인터페이스는 일단 속성(멤버 변수)이 없기 때문에
x
와 y
는 같은 타입으로 간주된다.
- 그런데 만약 다음과 같이 인터페이스에 속성이 있어서 제네릭의 타입 인자가 속성에 할당된다면 얘기는 다르다.
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number> = { data: 1 };
let y: NotEmpty<string> = { data: "a" };
x = y; // error
- 인터페이스
NotEmpty
에 넘긴 제네릭 타입 <T>
이 data
속성에 할당되었으므로 x
와 y
는 서로 다른 타입으로 간주된다.
References