📎아이템 37 공식 명칭에는 상표를 붙이기
구조적 타이핑의 특성 때문에 가끔 코드가 이상한 결과를 낼 수 있다.
📍 예제
🔗 벡터
이 코드는 구조적 타이핑 관점에서는 문제가 없지만, 수학적으로 따지면 2차원 벡터를 사용해야 이치에 맞다.
// 상표를 사용하지 않은 경우
interface Vector2D{
x: number;
y: number;
}
function calculateNorm(p: Vector2D){
return Math.sqrt(p.x * p.x + p.y * p.y);
}
calculateNorm({x: 3, y: 4}); // 결과는 5
const vec3D = {x: 3, y: 4, z: 1};
calculateNorm(vec3D); // 잘못된 입력이 들어갔지만, 결과는 5
calculateNorm 함수가 3차원 벡터를 허용하지 않게 하려면 공식 명칭을 사용하면 된다. 공식 명칭을 사용하는 것: 타입이 아니라 값의 관점에서 Vector2D라고 말하는 것이다. → 상표(brand)를 붙이면 된다. (스타벅스가 아니라 커피)
//상표를 사용한 경우
interface Vector2D{
_brand: '2d';
x: number;
y: number;
}
function vec2D(x: number, y: number): Vector2D{
return {x, y, _brand:'2d'}
}
function calculateNorm(p: Vector2D){
return Math.sqrt(p.x * p.x + p.y * p.y); // 기존과 동일
}
calculateNorm(vec2D(3, 4); // 결과는 5
const vec3D = {x: 3, y: 4, z: 1};
calculateNorm(vec3D); // 에러: '_brand' 속성이 없음
상표(_brand)를 사용해서 calculateNorm 함수가 Vector2D 타입만 받는 것을 보장한다.
상표 기법은 타입 시스템에서 동작하지만 런타임에 상표를 검사하는 것과 동일한 효과를 얻을 수 있다. 타입 시스템이기 때문에 런타임 오버헤드를 없앨 수 있고 추가 속성을 붙일 수 없는 string이나 nuber 같은 내장 타입도 상표화할 수 있다.
🔗 number 타입에 상표 붙이기
단위 붙이기
type Meters = number & {_brand: 'meters'};
type Seconds = number & {_brand: 'seconds'};
const meters = (m: number) => m as Meters;
const seconds = (s: number) => s as Seconds;
const oneKm = meters(1000);
const oneMin = seconds(60);
number 타입에 상표를 붙여도 산술 연산 후에는 상표가 없어지기 때문에 실제로 사용하기에는 무리가 있다.
const tenKm = oneKm * 10 // 타입이 number
const v = oneKm / oneMin; // 타입이 number
그러나 코드에 여러 단위가 혼합된 많은 수의 숫자가 들어 있는 경우, 숫자의 단위를 문서화하는 방법이 될 수 있다.
📍 요약
타입스크립트는 구조적 타이핑(덕 타이핑)을 사용하기 때문에 값을 세밀하게 구분하지 못하는 경우가 있다. 값을 구분하기 위해 공식 명칭이 필요하다면 상표를 붙이는 것을 고려해야한다.
상표 기법은 타입 시스템에서 동작하지만 런타임에 상표를 검사하는 것과 동일한 효과를 얻을 수 있다.
Last updated