본문으로 건너뛰기

240105

아이템. 8

interface Cylinder {
radius: number;
height: number;
}

const Cylinder = (radius: number, height: number): Cylinder => ({
radius,
height,
});

const calculateVolume = (shape: unknown) => {
if (shape instanceof Cylinder) {
return shape.radius; // '{}' 형식에 'radius' 속성이 없습니다.ts(2339)
}
};
  • instanceof를 이용해 shape가 Cylinder 타입인지 체크하려고 했음
  • 그러나, instanceof는 자바스크립트의 런타임 연산자이고, 값에 대해서 연산을 함
    • 그래서, instanceof Cylinder는 타입이 아니라 함수를 참조한다.
  • 한 심벌이 타입인지, 값인지는 언뜻 봐서 알 수 없음
  • 어떤 형태로 쓰이는지 문맥을 살펴 알아내야 함.

class Cylinder {
radius: 1;
height: 1;
}

const calculateVolume = (shape: unknown) => {
if (shape instanceof Cylinder) {
shape;
shape.height;
shape.radius; // 모두 정상
}
};
  • class와 enum은 상황에 따라 타입과 값 두 가지 모두 가능한 예약어다.
  • 클래스가 타입으로 쓰일 때는 형태(속성과 메서드)가 사용되는 반면, 값으로 쓰일 때는 생성자가 사용됨
interface Person {
first: string;
last: string;
}

const p: Person = {
first: "Lee",
last: "KeunHwee",
};

const email = (p: Person, subject: string, body: string): Response => {
// ...
};

type T1 = typeof p; // 타입이 Person
type T2 = typeof email; // 타입이 (p: Person, subject: string, body: string) => Response

const v1 = typeof p; // 타입이 "object"
const v2 = typeof email; // 타입이 "function"
  • 타입관점에서 typeof는 값을 읽어서 타입스크립트 타입으로 반환함
  • 값의 관점에서 typeof는 자바스크립트 런타임의 typeof 연산자가 됨

  • Cylinder 예제에서 본 것처럼 class 키워드는 값과 타입 두 가지로 모두 사용됨
  • 따라서 클래스에 대한 typeof는 상황에 따라 다르게 동작한다.
const v = typeof Cylinder; // 값은 function
type T = typeof Cylinder; // 타입은 typeof Cylinder

두 번째 줄의 타입은 무슨 의미일까?

  • 여기서 중요한 것은 Cylinder가 인스턴스의 타입이 아니라는 점이다.
  • 실제로 new 키워드를 사용할 때 볼 수 있는 생성자 함수임
declare let fn: T; // fn의 타입은 typeof Cylinder
const c = new fn(); // c의 타입은 Cylinder
  • 다음 코드처럼 InstanceType 제네릭을 사용해 생성자 타입과 인스턴스 타입을 전환할 수 있다.
type C = InstanceType<typeof Cylinder>; // 타입은 Cylinder

  • 값에서 &와 |는 AND와 OR 비트연산이지만, 타입에서는 인터섹션과 유니온이다.
  • 타입스크립트 코드가 잘 동작하지 않는다면 타입 공간과 값 공간을 혼동해서 잘못 작성했을 가능성이 크다.