타입스크립트 4.9 버전에 안전한 업캐스팅을 지원하는 satisfies 연산자를 공개했다. satisfies 연산자를 활용하면 타입을 변경하지 않고도 변수에 제약 조건(constraint)을 적용할 수 있다. 즉, 객체 프로퍼티에 제약 조건을 적용하면서 각 프로퍼티의 타입을 올바르게 추론할 수 있다.

<aside> 💡 용어 설명

문제


아래 참가자 정보를 나타내는 attendee 객체가 있다. 객체를 선언하면 타입스크립트는 객체의 각 프로퍼티 타입을 자동으로 추론한다.

그래서 attendee.age(number 타입으로 추론)에 숫자를 더하거나 attendee.name(string 타입으로 추론)에 toUpperCase 같은 메서드를 사용할 수 있다. 하지만 attendee 객체가 어떤 프로퍼티로 구성돼 있는지 구체적으로 명시하지 않아서(제약 조건 없음) posittion 같은 오타가 생겨도 에러가 발생하지 않는다.

// attendee: { name: string; age: number; posittion: string; }
const attendee = {
  name: 'Smith',
  age: 30,
	posittion: 'Engineer' // 오타
};

const plusAge = attendee.age + 10; // OK (number 타입으로 추론)
const nameToUpperCase = attendee.name.toUpperCase(); // OK (string 타입으로 추론)

타입 어노테이션(타입 표기)을 사용하면 위 같은 오타를 사전에 방지할 수 있다. 하지만 객체의 각 프로퍼티가 어떤 타입을 가지는지에 대한 정보를 잃게 된다. 결국 attendee 객체의 모든 프로퍼티는 string | number 유니온 타입을 갖게 된다.

type Attendee = 'name' | 'age' | 'position';

const attendee: Record<Attendee, string | number> = {
  name: 'Smith', 
  age: 30,
  posittion: 'Engineer' // Error! 오타 감지
};

const plusAge = attendee.age + 10; // Error! ts(2365)
const nameToUpperCase = attendee.name.toUpperCase(); // Error! ts(2339)

때문에 attendee.age에 10을 더하면 string | number 타입에 + 연산자를 사용할 수 없다고 나온다. 마찬가지로 attendee.nametoUpperCase를 사용하면 string | number 타입에 toUpperCase 속성이 없다는 에러가 발생한다.

해결


위 같은 문제는 satisfies 연산자를 사용하면 해결할 수 있다. 그럼 attendee 객체 프로퍼티를 Attendee 타입으로 제한하면서(제약 조건 적용), 각 프로퍼티의 타입을 올바르게 추론할 수 있다.

type Attendee = 'name' | 'age' | 'position';

const attendee = {
  name: 'Smith',
  age: 30,
  posittion: 'Engineer', // Error! 오타 감지
} satisfies Record<Attendee, string | number>;

const plusAge = attendee.age + 10; // OK (number 타입으로 추론)
const nameToUpperCase = attendee.name.toUpperCase(); // OK (string 타입으로 추론)

예시 — unknown

unknown 키워드를 사용했을 때도 프로퍼티 타입을 올바르게 추론한다. satisfies 연산자를 사용하지 않으면 각 프로퍼티 값을 사용할 때마다 타입 단언/가드 작업을 해줘야 한다.

type Attendee = 'name' | 'age' | 'position';

const attendee = {
  name: 'Smith',
  age: 30,
  posittion: 'Engineer', // Error! 오타 캐치
} satisfies Record<Attendee, unknown>;

const plusAge = attendee.age + 10; // OK
const nameToUpperCase = attendee.name.toUpperCase(); // OK

Untitled