/** brand를 적용하지 않을 때 발생하는 문제 상황 */
interface Vercotr2D {
x: number;
y: number;
}
function calculateNorm(p: Vector2D) {
return Math.sqrt(p.x * p.x + p.y * p.y);
}
calculateNorm({x: 3, y: 4}); // 문제없는 코드
const vec3D = {x: 3, y: 4, z: 1};
calculateNorm(vec3D); // 구조적 타이핑 관점으로 문제없는 코드
/** brand를 적용한 상황 */
interface Vector2D {
_brand: '2d'; // 상표를 사용해서 calculateNorm 함수가 Vector2D 타입만 받는 것을 보장.
x: number;
y: number;
}
// x와 y값을 받아 brand를 붙여 Vector2D 타입으로 만들어주는 함수
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));
const vec3D = {x: 3, y: 4, z: 1}; // 여기에 _brand를 추가한다면 어쩔 수 없음.. 적어도 실수 방지는 할 수 있다.
calculateNorm(vec3D);
// 추가 속성을 붙일 수 없는 string이나 number 같은 내장 타입 상표화 가능
// 온전히 타입 시스템의 영역
type AbsolutePath = string & { _brand: 'abs' }; // 절대 경로 타입
function listAbsolutePath(path: AbsolutePath) {
// ...
}
// 절대 경로, 상대 경로 판단 함수
// isAbsolutePath 함수의 리턴값이 true이면
// 함수를 호출한 범위 내에서 path의 타입은 AbsolutePath이다.
function isAbsolutePath(path: string): path is AbsolutePath {
return path.startsWith('/');
}
// path에 절대 경로와 상대 경로 둘 다 가능하다면
// 타입 정제를 해주는 타입 가드를 사용해서 오류 방지!
function f(path: string) {
if (isAbsolutePath(path)) {
listAbsoultePath(path); // 오류가 발생하지 않는다.
}
listAbsolutePath(path); // 오류가 발생한다. path가 string이기 때문
}
🔍 타입 가드란? 특정 범위 안에서 런타임 타입 검사를 수행하는 표현식이며 컴파일러가 타입을 예측할 수 있도록 코드를 작성해서 버그가 발생하지 않도록 예방하는 방법이다. 하지만 여기에서는 단순이 타입을 방어한다는 느낌으로 작성된 것 같다.
type SortedList<T> = T[] & { _brand: 'sorted' };
// 정렬이 되어있는지 목록 전체를 루프 도는 방법 (효율적 X. 안정성 확 O)
function isSorted<T>(xs: T[]): xs is SortedList<T> {
for(let i = 1; i < xs.length; i++) {
if(xs[i] < xs[i-1]) {
return false;
}
}
return true;
}
// 이진 검색을 하는 함수
// 아래처럼 정렬되었다는 상표가 붙은 SortedList 타입 값을 호출하거나
// isSorted를 호출하여 정렬되었음을 증명하거나
function binarySearch<T>(xs: SortedList<T>, x: T): boolean {
// ...
}
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); // 타입이 Meters
const oneMin = seconds(60); // 타입이 Seconds
const tenKim = oneKm * 10; // 타입이 number
const v = oneKm / oneMin; // 타입이 number
좋은 정보 감사합니다!