타입스크립트에서 구조적 타이핑으로 인해 유니온 타입의 논리가 조금 이상한데 구조적 타이핑, 덕 타아핑의 한계로 인해 어쩔 수 없는 것인가요?

반응형

 

타입스크립트에서 구조적 타이핑으로 인해 유니온 타입의 논리가 조금 이상한데 이유가 뭘까요? 다음 예시를 보면 오류를 내지 않고 있어요. 그래서 유사한 구조의 타입들을 구분하기 위해서는 Discriminated Unions 를 사용하라고 하는데요. 애초에 유니온을 만들때 부터 정확한 논리구조로 만들 수는 없는 것이었을까요? 구조적 타이핑, 덕 타아핑의 한계로 인해 어쩔 수 없는 것인가요?

 

다음의 예시에서 data 는 a, b, c, d 속성을 모두 가지고 있음에도 에러를 내지 않는다.

 

type A = {
  a: string;
  b: string;
}
type B = {
  a: string;
  b: string;
  c: number;
}
type C = {
  a: string;
  b: string;
  d: () => void;
}
type ABC = A | B | C;

const data: ABC[] = [
  { 
    a: 'a', 
    b: 'b', 
    c: 123, 
    d: () => {} 
  },
];

 

 

 

 

 

타입스크립트에서 발생하는 문제는 **구조적 타이핑(Structural Typing)**과 관련이 있으며, 이로 인해 유니온 타입의 논리가 기대한 대로 작동하지 않을 수 있습니다. 이 문제를 이해하기 위해서는 구조적 타이핑의 기본 개념과 덕 타이핑(Duck Typing)의 특성을 먼저 살펴볼 필요가 있습니다.

1. 구조적 타이핑과 덕 타이핑

타입스크립트는 구조적 타이핑(Structural Typing) 시스템을 사용합니다. 이는 객체의 타입이 해당 객체의 구조에 의해 결정된다는 것을 의미합니다. 객체가 특정 인터페이스나 타입을 구현하고 있는지를 판단할 때, 타입스크립트는 객체가 그 구조를 가지고 있는지만 확인합니다. 이와 유사한 개념이 덕 타이핑인데, "오리가 걷고, 수영하고, 꽥꽥거린다면 그것은 오리다"라는 논리로 설명됩니다. 즉, 객체가 특정 구조나 메서드를 가지고 있으면 그 객체는 해당 타입으로 간주됩니다.

2. 유니온 타입의 동작

유니온 타입(A | B | C)은 여러 타입 중 하나를 가질 수 있는 타입입니다. 타입스크립트에서 유니온 타입의 요소가 되는 각각의 타입이 어떤 속성을 가지는지를 기준으로 객체의 타입을 확인합니다.

타입스크립트에서는 ABC라는 유니온 타입이 정의되었을 때, ABC 타입의 객체는 A, B, C 타입 중 하나로 간주될 수 있습니다. 이 때, 객체가 A 또는 B 또는 C 타입의 구조를 만족하기만 하면 그 객체는 유효한 타입으로 인정됩니다.

3. 문제의 예시 분석

type A = {
  a: string;
  b: string;
}
type B = {
  a: string;
  b: string;
  c: number;
}
type C = {
  a: string;
  b: string;
  d: () => void;
}
type ABC = A | B | C;

const data: ABC[] = [
  { 
    a: 'a', 
    b: 'b', 
    c: 123, 
    d: () => {} 
  },
];



위 예시에서, data 배열에 포함된 객체는 a, b, c, d 네 가지 속성을 모두 가지고 있습니다. 그런데도 타입스크립트는 이 객체를 ABC 유니온 타입 중 하나로 간주합니다. 그 이유는 구조적 타이핑에 따라, 이 객체가 A, B, C 중 하나의 구조를 충족시키면 타입 체크에서 유효하다고 판단하기 때문입니다.

  • 이 객체는 a와 b 속성을 가지고 있으므로 A의 구조를 만족합니다.
  • 이 객체는 a, b, c 속성을 가지고 있으므로 B의 구조도 만족합니다.
  • 이 객체는 a, b, d 속성을 가지고 있으므로 C의 구조도 만족합니다.

따라서 타입스크립트는 이 객체가 A | B | C 타입 중 어느 하나에 해당한다고 판단하고, 에러를 발생시키지 않습니다.

4. Discriminated Unions (태그된 유니온)

유사한 구조의 타입을 확실히 구분하고 싶을 때는 Discriminated Unions(태그된 유니온)을 사용하는 것이 좋습니다. 이 방법에서는 공통 속성으로 구별자(discriminator)를 추가하여 타입을 명시적으로 구분할 수 있습니다. 예를 들어:

 

type A = {
  kind: 'A';
  a: string;
  b: string;
}
type B = {
  kind: 'B';
  a: string;
  b: string;
  c: number;
}
type C = {
  kind: 'C';
  a: string;
  b: string;
  d: () => void;
}
type ABC = A | B | C;

const data: ABC[] = [
  { 
    kind: 'A', // 명시적으로 A 타입임을 표시
    a: 'a', 
    b: 'b', 
    c: 123, 
    d: () => {} 
  }, // 이제 에러가 발생할 것입니다.
];

 

이렇게 kind 속성을 사용하여 각 타입을 명확히 구분하면, 타입스크립트는 해당 객체가 실제로 A, B, C 중 어느 타입에 해당하는지를 명확히 판단할 수 있습니다.

5. 결론: 구조적 타이핑의 한계

구조적 타이핑은 매우 유연하고 강력하지만, 유사한 구조를 가진 타입들을 구분하는 데 어려움이 있을 수 있습니다. 타입스크립트는 객체의 구조만으로 타입을 판단하기 때문에, 유니온 타입에서 기대한 대로 동작하지 않을 수 있습니다.

이러한 한계를 극복하기 위해 Discriminated Unions 같은 방법이 사용됩니다. 하지만, 이는 구조적 타이핑과 덕 타이핑의 본질적인 특성에서 기인하는 것으로, 타입스크립트의 설계 철학에 맞게 유연성을 제공하면서도 안전성을 높이기 위한 해결책입니다.

 

 

 

반응형

댓글

Designed by JB FACTORY