반응형
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

구조적 타이핑 본문

TS/이펙티브 타입스크립트

구조적 타이핑

5_hyun 2023. 1. 17. 01:25
반응형

자바스크립트는 덕 타이핑(duck typing) 기반이다. 덕 타이핑은 "만약 어떤 새가 오리처럼 걷고, 헤엄치고, 짹짹거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다."라는 명제이다. 예를 들어 "안녕"이라는 문자열에 숫자 10을 더하면 "안녕10" 이렇게 나오는 것을 의미한다.

구조적 타이핑

성공 예시

interface Vector2D {
  x: number;
  y: number;
}
function calculateLength(v: Vector2D) {
  return Math.sqrt(v.x * v.x + v.y * v.y);
}
interface NamedVector {
  name: string;
  x: number;
  y: number;
}

const v: NamedVector = { x: 3, y: 4, name: "Zee" };
console.log(calculateLength(v)); // 5

calculateLength의 매개변수 타입은 Vector2D라서 name이 정의되지 않다. 실제 인자에 name이 포함되어 있는 값을 넣어도 코드는 에러가 발생하지 않고 돌아간다. Vector2D와 NamedVector의 관계를 선언하지 않았는데도 x, y가 있어서 서로 호환되어 잘 돌아갔다. 하지만 이런 구조적 타이핑 때문에 문제가 발생하기도 한다.

문제 발생 예시

-예시1

interface Vector2D {
  x: number;
  y: number;
}

function calculateLength(v: Vector2D) {
  return Math.sqrt(v.x * v.x + v.y * v.y);
}

interface Vector3D {
  x: number;
  y: number;
  z: number;
}

function normalize(v: Vector3D) {
  const length = calculateLength(v);
  return {
    x: v.x / length,
    y: v.y / length,
    z: v.z / length,
  };
}

console.log(normalize({ x: 3, y: 4, z: 5 }));
// {x: 0.6, y: 0.8, z: 1}

normalize는 벡터의 길이를 1로 만드는 정규화 함수이다. 하지만 결과가 잘못 출력된다. 그 이유는 calculateLength의 매개변수는 Vector2인데 실제 사용되는 값은 Vector3라서 z가 무시되기 때문이다. Vector3는 x, y가 있어서 Vector2와 호환이 되기 때문에 타입 체커가 문제로 인식하지 않는다.

함수의 매개변수가 정의된 타입 속성만 가질 것이라고 생각하기 쉽다. 이런 것을 "봉인된(sealed)" 혹은 "정확한(precise)" 타입이라 불린다. TS에서는 이런 타입들을 표현할 수 없고 열려있는 타입을 사용한다. 타입이 열려 있다는 것은 타입 확장에 열려있다는 의미이다. 따라서 정의되지 않은 타입을 추가해도 오류가 발생하지 않는다.

 

-예시2

interface Vector2D {
  x: number;
  y: number;
}

interface Vector3D {
  x: number;
  y: number;
  z: number;
}

// 에러가 나는 코드
function calculateLengthL1(v: Vector3D) {
  let length = 0;

  for (const axis of Object.keys(v)) {
    const coord = v[axis];

    length += Math.abs(coord);
  }
  return length;
}

// 정상 출력되는 코드
function calculateLengthL2(v: Vector3D) {
  return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z);
}

const vec3D = { x: 3, y: 4, z: 1, address: "123 Broadway" };

console.log(calculateLengthL1(vec3D));
// NaN
console.log(calculateLengthL2(vec3D));
// 8

calculateLength1은 axis가 sting도 될 수 있기 때문에 NaN이 출력된다. 이렇게 타입이 각각 다른 경우에는 필요한 속성을 각각 더해주는 것이 좋다. 따라서 calculateLength2 처럼 각각 필요한 것을 더해주는 것이 좋는 방법이다.

반응형

'TS > 이펙티브 타입스크립트' 카테고리의 다른 글

타입 선언문과 단언문  (0) 2023.02.10
타입과 값 구분하기  (0) 2023.02.10
TS의 타입  (0) 2023.01.21
ts와 타입의 관계 이해  (0) 2023.01.04
TS와 JS, TS 설정  (0) 2023.01.02
Comments