프로그래밍풀스택 개발자

TypeScript에서 구조적 타입 호환성(Structural typing)은 어떻게 작동합니까? 명명적 타입과의 차이점은 무엇이며, 그 강점은 무엇인지, 어떤 함정이 있는지 설명하십시오.

Hintsage AI 어시스턴트로 면접 통과

답변.

질문의 역사

많은 객체 지향 언어와는 달리 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, 타입은 구조적으로 호환됨

주요 내용:

  • 타입 이름이 아닌 구조(속성과 그 타입)에 의한 확인
  • 기존 JS 코드와의 통합 용이성
  • 서로 다른 의미를 가진 객체의 구조 일치 시 발생할 수 있는 충돌

장난스러운 질문들.

두 인터페이스의 구조가 일치하면, 그것이 완전히 상호 교환 가능하다는 의미입니까?

구조상으로는 그렇지만, 프로그램의 논리상으로는 아닙니다. 이는 컴파일러 수준에서 허용되지만, 논리적 오류를 초래할 수 있습니다(예: 위의 Point와 Pixel).

어떤 타입에 대한 구조적 호환성을 금지할 수 있습니까?

완전히는 불가능하지만, 고유 속성을 추가하여 가능하게 할 수 있습니다(예: 기호 사용):

interface Brand { _brand: unique symbol; }

이제 다른 객체는 동일한 고유 기호 없이 이 타입을 모방할 수 없습니다.

구조적 타입화는 명명적 타입화와 어떻게 다른가요?

구조적은 구조의 존재에 따라, 명명적은 특정 네임스페이스에서 타입 이름이 일치하는 것에 따라 결정됩니다. TypeScript는 기본적으로 항상 구조적 타입을 사용합니다.

일반적인 오류와 안티 패턴

  • 동일한 구조를 가진 관련 없는 객체를 잘못 수용하는 것
  • 시그니처가 완전히 일치하는 경우 — 개념적으로 다른 타입을 분리할 수 없음
  • 타입의 안전성에 대한 잘못된 느낌

실제 사례

부정적 사례

여러 개체에서 unintentionally 같은 필드가 맞춰지는 경우(예: User와 Admin이 {id: number, name: string}와 같은 구조를 가질 수 있음)로 인해 API 계약 작업 시 혼란을 초래할 수 있습니다.

장점:

  • 적은 코드로 새로운 개체를 확장하기 쉬움

단점:

  • 컴파일러가 잡지 않는 논리적 오류를 추적하기 어려움

긍정적 사례

동일한 구조를 가진 의미적으로 다른 타입을 구별하기 위해 고유한 ‘라벨(grant)’ 기호와 비표준 필드를 사용하는 것.

장점:

  • 비즈니스 논리에 따라 명확하게 구분됨
  • 개념의 추가적인 안전성과 명확성 제공

단점:

  • 코드의 추가적인 복잡성
  • 타입에 대한 더욱 철저한 계획이 필요함