이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍덕 타이핑(duck typing) 이란?
- 객체의 변수 및 메서드 집합이 타입을 결정
즉, 객체가 어떤 타입에 부합하는 변수와 메서드를 가질 경우, 객체를 해당 타입에 속하는 것으로 간주하는 것
- 많은 새들 중에서 오리처럼 걷고, 헤엄치고, 꽥꽥거리면 그 새를 오리라고 할 수 있는 것에서 유래
📍구조적 타이핑(structural typing)
- 타입 구조가 유사하면 (ex. 객체의 프로퍼티들이 비슷) 다른 두 타입이 서로 호환될 수 있는 것
- JS가 덕 타이핑 기반이고, TS는 JS의 런타임 동작을 모델링하기 때문에, 구조적 타이핑 발생
- 예시
Vector2D 타입과 NamedVector 간 관계를 선언하지 않았는데 호환이 가능
interface Vector2D {
x: number;
y: number;
}
// 함수 선언시 매개변수를 Vector2D 타입으로 설정
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",
};
// 그렇지만 NamedVector 타입의 매개변수도 문제없이 사용 가능
// 별도의 calculateLength 함수를 정의할 필요 없음
console.log(calculateLength(v)); // 5
=> 이는 타입스크립트의 타입이 open 되어 있다(타입의 확장에 열려 있다)는 것을 잘 보여줌
타입에 선언된 속성 이외에 임의의 속성을 추가하더라도 오류 발생 X
- 구조적 타이핑(타입의 확장에 열려 있다)으로 인한 문제 예시
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]; // 에러 : axis 에 문자열이 올 수 있음
length += Math.abs(coord);
}
return length;
}
위 코드의 반례
interface Vector3D {
x: number;
y: number;
z: number;
}
const example = {
x: 3,
y: 4,
z: 5,
text: "type" // 문자열 필드가 추가됨
}
function calculateLengthL1(v: Vector3D) { // 매개변수 각 필드의 합을 구하는 함수
let length = 0;
for (const axis of Object.keys(v)) {
const coord = v[axis]; // 에러 : axis 에 문자열이 올 수 있음
length += Math.abs(coord);
}
return length;
}
calculateLengthL1(example) // 타입 에러 없음!
/*
const example: {
x: number;
y: number;
z: number;
text: string;
}
인데, Vector3D가 되어야 하는 calculateLengthL1의 매개변수가 될 수 있음
-> 덕 타이핑의 문제
*/
위의 함수를 이렇게 고쳐야함
- 각각의 값에 대해 타입이 숫자가 되게 지정
function calculateLengthL2(v: Vector3D) {
return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z);
}