Typescript의 타입 호환성은 구조적 서브타이핑을 기반으로 합니다. 구조적 타이핑이란 오직 멤버 만으로 타입을 관계시키는 방식입니다. 명목적 타이핑과는 대조적입니다.
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// 성공, 구조적 타이핑이기 때문입니다.
p = new Person();
<aside> 💡 명목상 이름을 보는 것이 아니라 멤버 구조를 보고 타입을 맞춰보고 관계 시키는구나!
</aside>
C# 이나 Java같은 명목적-타입 언어에서는 Person
클래스는 Named
인터페이스를 명시적으로 구현체로 기술하지 않았기 때문에 해당 코드는 오류를 발생 시킵니다.
Typescript의 구조적 타입 시스템은 Javascript 코드의 일반적인 작성 방식에 따라서 설계되었습니다. Javascript는 함수 표현식이나 객체 리터럴 같은 익명 객체를 광범위하게 사용하기 때문에 Javascript에서 발견되는 관계의 타입을 명목적 타입 시스템보다는 구조적 타입 시스템을 이용하여 표현하는 것이 훨씬 자연스럽습니다.
Typescript의 타입 시스템은 컴파일 시간에 확인할 수 없는 특정 작업을 안전하게 수행할 수 있습니다. 타입 시스템이 이런 특성을 갖고 있을 때, "건전" 하지 않다고 말합니다. Typescript에서 건전하지 못한 곳을 허용하는 부분을 신중하게 고려했으며, 이 문서 전체에서 이러한 상황이 발생하는 곳과 유발하는 시나리오에 대해 설명합니다.
Typescript의 구조적 타입 시스템의 기본 규칙은 y 가 최소한 x 와 동일한 멤버를 가지고 있다면 x 와 y 는 호환 된다는 것입니다.
interface Named {
name: string;
}
let x: Named;
// y의 추론된 타입은 { name: string; location: string; } 입니다.
let y = { name: "Alice", location: "Seattle" };
x = y;
<aside>
💡 y = x
의 경우 에러가 발생한다. x
가 location
을 가지고 있지 않기 때문이다.
</aside>
y는 location 프로퍼티를 추가적으로 가지고 있지만 오류를 발생시키지 않는 점에 유의합니다. 호환성을 검사할 때는 대상 타입(이 경우는 Named)의 입장에서만 프로퍼티를 검사합니다.