본문으로 건너뛰기

240104

아이템. 7

  • '할당 가능한 값들의 집합'이 타입이라고 생각할 것
  • 이 집합은 타입의 범위라고 부르기도 함
  • 가장 작은 집합은 아무 값도 포함하지 않는 공집합이며, 타입스크립트에서는 never타입이다.
    • never타입으로 선언된 변수의 범위는 공집합이기 때문에 아무런 값도 할당할 수 없다.
const x: never = 12;
// 'number' 형식은 'never' 형식에 할당할 수 없습니다.ts(2322)

  • 그 다음으로 작은 집합은 한 가지 값만 포함하는 타입 (=유닛타입)
  • 두 개 혹은 세 개로 묶으려면 유니온 타입을 사용함
type AB = "A" | "B";
type AB12 = "A" | "B" | 12;

const a: AB = "A"; // 정상, A는 집합 {"A", "B"}의 원소이다.
const c: AB = "C"; // '"C"' 형식은 'AB' 형식에 할당할 수 없습니다.ts(2322)
  • 여기서 c는 유닛타입이며, 범위는 단일 값 "C"로 구성되어 AB의 부분 집합이 아니므로 오류이다.
const ab: AB = Math.random() < 0.5 ? "A" : "B"; // 정상
const ab12: AB12 = ab; // 정상

type AB = "A" | "B";
type AB12 = "A" | "B" | 12;

const a: AB = "A"; // 정상, A는 집합 {"A", "B"}의 원소이다.
const c: AB = "C"; // '"C"' 형식은 'AB' 형식에 할당할 수 없습니다.ts(2322)

const ab: AB = Math.random() < 0.5 ? "A" : "B"; // 정상
const ab12: AB12 = ab; // 정상

declare let twelve: AB12;
const back: AB = twelve; // 'AB12' 형식은 'AB' 형식에 할당할 수 없습니다.
// '12' 형식은 'AB' 형식에 할당할 수 없습니다.ts(2322)

interface Person {
name: string;
}
interface Lifespan {
birth: Date;
death?: Date;
}
type PersonSpan = Person & Lifespan;

const ps: PersonSpan = {
name: "Alan Turing",
birth: new Date("1912/06/23"),
death: new Date("1954/06/07"),
}; // 정상
  • & 연산자는 두 타입의 인터섹션(교집합)을 계산함
  • 언뜻보기엔, 두 타입의 교집합이 존재하지 않기 때문에 공집합으로 예상하기 쉬우나, 타입 연산자는 인터페이스의 속성이 아닌, 값의 집합(타입의 범위)에 적용됨
  • 추가적인 속성을 가지는 값도 여전히 그 타입에 속하게 된다. 그래서 Person과 Lifespan을 둘 다 가지는 값은 인터섹션 타입에 속하게 됨

type K = keyof (Person | Lifespan); // never
  • 앞의 유니온 타입에 속하는 값은 어떠한 키도 없기 때문에, 유니온에 대한 keyof는 공집합이어야만 한다.
type K = keyof (Person & Lifespan); // "name" | "birth" | "death"
  • 약간 개념을 반대로 생각하면 됨
    • 유니온은 합집합 같지만 범위로 따졌을 때 교집합 keyof (A&B) = (keyof A) | (keyof B)
    • 인터섹션은 교집합 같지만 범위로 따졌을 때 합집합 keyof (A|B) = (keyof A) & (keyof B)

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

type PointKeys = keyof Point; // "x" | "y"
function sortBy<K extends keyof T, T>(vals: T[], key: K) {
//..
}

const pts: Point[] = [
{x: 1, y: 2},
{x: 0, y: 0},
];
sortBy(pts, "x");
sortBy(pts, "y");
sortBy(pts, "z"); // '"z"' 형식은 'Point' 형식에 없습니다.ts(2345)

요약

effective-typescript

  • 타입 연산은 집합의 범위에 적용된다. A와 B의 인터섹션은 A의 범위와 B의 범위의 인터섹션이다.
  • 객체 타입에서는 A & B인 값이 A와 B의 속성을 모두 가짐을 의미한다.