enum 보다 union type을 사용하자

Feb 4, 2022

union type을 사용하는게 좋은 이유

enum을 쓰는 경우

// fruit.ts
export enum Fruit {
  Apple = 'Apple',
  Orange = 'Orange',
  Grape = 'Grape',
}
// import_enum.ts
import { Fruit } from './fruit';

const fruit: Fruit = Fruit.Apple;
console.log(fruit);

이를 tsc로 트랜스파일링 후 rollup으로 번들링하면 아래와 같이 변환된다.

var Fruit;
(function (Fruit) {
  Fruit['Apple'] = 'Apple';
  Fruit['Orange'] = 'Orange';
  Fruit['Grape'] = 'Grape';
})(Fruit || (Fruit = {}));

var fruit = Fruit.Apple;
console.log(fruit);

union type을 쓰는 경우

// fruit.ts
export type Fruit = 'Apple' | 'Orange' | 'Grape';
// import_union.ts
import { Fruit } from './fruit';

const fruit: Fruit = 'Apple';
console.log(fruit);

이를 트랜스파일링 및 번들링 하면 아래와 같다.

var fruit = 'Apple';
console.log(fruit);

union type을 쓰는 경우 장점

  1. js로 변환시 코드가 줄어든다. enum은 js로 트랜스파일링 될때 타입과는 달리 사라지지 않기 때문에 코드양을 증가시킨다.
  2. enum과 달리 import를 하지 않아도 사용할 수 있다. (컴포넌트의 props 등에 지정되어 있을때)
  3. enum보다 선언 방법이 간단하다. ts↦type Fruit = 'Apple' | 'Orange' | 'Grape'
  4. union type을 사용해도 ide (e.g. vscode)의 auto complete 기능이 활성화된다.

같이 보면 좋은 tsconfig.json 옵션

esModuleInterop (default: false)

이 옵션이 false 또는 not set 이라면 CommonJS/AMD/UMD 모듈을 ESM처럼 취급한다.
문제가 되는 경우는 아래처럼 CJS 모듈을 ESM 코드베이스로 가져오려고 할 때다.

// node_modules/moment/index.js
exports = moment;
// app/index.ts
import * as moment from 'moment';
moment(); // moment는 순수 객체만 될 수 있어서 이렇게 사용하는 것은 ESM 스펙을 준수하지 않음!

// 트랜스파일링된 결과
const moment = require('moment');
moment();

esModuleInterop를 활성화하면 ESM 사양에 따라 CJS 모듈을 가져올 수 있다.

// app/index.ts
import moment from 'moment';
moment(); // ESM 스펙을 준수함

// 트랜스파일링된 결과
const moment = __importDefault(require('moment'));
moment.default();

isolatedModules (default: false)

Babel이나 다른 빌드툴은 한번에 하나의 하나의 파일에서만 작동하므로 const enum이나 namespace같은 타입스크립트 기능을 사용하는데 런타임 문제가 발생할 수 있다. 이 옵션을 true로 설정하면 단일 파일 변환 프로세스에서 올바르게 해석할 수 없는 코드를 작성할 경우 경고를 받게 된다.
대표적으로 모든 실행 파일들은 모듈이어야 한다는 규칙이 있다.

importsNotUsedAsValues (default: remove)

import가 어떻게 처리되는지를 결정하는 플래그이다.

  • remove : 참조 타입만 drop
  • preserve : 사용되지 않는 값이나 타입들도 전부 보존한다. side-effects를 일으킬 수도 있음
  • error : 모든 import를 보존하지만, value import가 값처럼 사용되면 error을 출력

preserveValueImports (default: false)

타입스크립트가 import를 사용하고 있음을 감지하지 못하는 경우가 있다. Svelte나 Vue처럼 Compiles to HTML 언어를 사용하거나 아래와 같은 경우다.

import { Animal } from './animal.js';

// eval에서 Animal을 사용하지만 string이므로 인식하지 못한다
eval('console.log(new Animal().isDangerous())');

isolatedModules 옵션과 함께 사용되는 경우, import 된 타입은 컴파일러가 이 import 가 사용되지 않는 값인지 또는 삭제되어야 하는 type 인지 알 수 없기 때문에 type-only로 지정해야 한다.

// ts 4.5부터 modifier를 붙일 수 있음
import { TitleComponent, type TitleComponentProps } from './TitleComponent.js';

참고