많은 객체 지향 언어와는 달리 TypeScript는 구조적 타입화(duck typing)를 구현합니다: 객체는 명시적으로 해당 타입으로 선언되었는지 여부와 관계없이 필요한 모든 속성을 가지고 있다면 해당 타입과 일치하는 것으로 간주됩니다.
이 유연성은 때때로 객체의 구조가 일치할 경우, 실제로는 의미적으로 관련이 없더라도 객체가 타입으로 간주되는 예기치 않은 상황을 초래합니다. 이는 구조가 우연히 일치하는 복잡한 데이터 모델에서 위험할 수 있습니다.
항상 객체의 타입을 올바르게 구조화하고, 서로 다른 개체의 구조 일치를 최소화하며, 중요한 경우 ‘명명화’ 타입을 위해 추가 속성이나 기호를 사용하십시오.
코드 예시:
interface Point { x: number; y: number; } interface Pixel { x: number; y: number; } function drawPoint(p: Point) { console.log(p.x, p.y); } const pixel: Pixel = { x: 1, y: 2 }; drawPoint(pixel); // OK, 타입은 구조적으로 호환됨
주요 내용:
두 인터페이스의 구조가 일치하면, 그것이 완전히 상호 교환 가능하다는 의미입니까?
구조상으로는 그렇지만, 프로그램의 논리상으로는 아닙니다. 이는 컴파일러 수준에서 허용되지만, 논리적 오류를 초래할 수 있습니다(예: 위의 Point와 Pixel).
어떤 타입에 대한 구조적 호환성을 금지할 수 있습니까?
완전히는 불가능하지만, 고유 속성을 추가하여 가능하게 할 수 있습니다(예: 기호 사용):
interface Brand { _brand: unique symbol; }
이제 다른 객체는 동일한 고유 기호 없이 이 타입을 모방할 수 없습니다.
구조적 타입화는 명명적 타입화와 어떻게 다른가요?
구조적은 구조의 존재에 따라, 명명적은 특정 네임스페이스에서 타입 이름이 일치하는 것에 따라 결정됩니다. TypeScript는 기본적으로 항상 구조적 타입을 사용합니다.
여러 개체에서 unintentionally 같은 필드가 맞춰지는 경우(예: User와 Admin이 {id: number, name: string}와 같은 구조를 가질 수 있음)로 인해 API 계약 작업 시 혼란을 초래할 수 있습니다.
장점:
단점:
동일한 구조를 가진 의미적으로 다른 타입을 구별하기 위해 고유한 ‘라벨(grant)’ 기호와 비표준 필드를 사용하는 것.
장점:
단점: