JS, TS deep immutable types 만들기

00_8_3·2022년 3월 31일
0

typescript

목록 보기
1/8

TS에선 불변성이던 나 JS에선 아니야

배열 또는 객체가 immutable하여 외부의 영향을 안받는다는 확신이 필요할 때가 있습니다.
그러기 위해 TS에서는 as const 구문을 많이들 사용합니다.
const assertion이라고 불리는 것으로
객체를 readonly 상태 immutable한 객체로 만들어 줍니다.

하지만 컴파일 타임에만 해당하는 것이고
JS가된 파일을 보면 immutable을 보장하지 않는 것을 볼수 있습니다.

목적은 런타임에도 객체 동결을 만드는 것입니다.

JS, Object.freeze

JS에는 Object.freeze라는 훌륭한 객체 메소드가 있습니다.

얕은 동결

obj = {
  internal: {}
};

Object.freeze(obj);
obj.internal.a = 'aValue';

obj.internal.a // 'aValue'

하지만 객체의 깊이 1 까지만 동결을 해주기 때문에
재귀 함수를 통해 객체 내부 전체를 동결 시켜야 합니다.

function deepFreeze<T>(obj: T) {
  var propNames = Object.getOwnPropertyNames(obj);
  for (let name of propNames) {
    let value = (obj as any)[name];
    if (value && typeof value === "object") {
      deepFreeze(value);
    }
  }
  return Object.freeze(obj);
};

const bill = deepFreeze({
  name: "Bill",
  profile: {
    level: 1,
  },
  scores: [90, 65, 80],
} as const);

이제 런타임에도 객체나 배열을 deep immutable 상태로 만들어 사용할 수 있게되었습니다.

Immutable Type

위의 방법으로 충분히 deep immutable이 되었습니다.
하지만 TS에는 타입 추론이라는 것이 존재하는데
명시적으로 bill이 동결상태라는 것을 어떻게 나타낼 수 있을까요?

type Person = {
  readonly name: string;
  readonly profile: {
    readonly level: number;
  };
  readonly scores: number[];
};

위와 같이 귀찮게도 모든타입에 readonly를 적어주어야 할까요?
운이 좋게도 재귀적인 type을 만들어 주면 그럴 필요가 없습니다.

...

type Immutable<T> = {
  readonly [K in keyof T]: Immutable<T[K]>;
};

const bill: Immutable<Person> = deepFreeze({
  name: "Bill",
  profile: {
    level: 1,
  },
  scores: [90, 65, 80],
});

위와 같이 사용하여 런타임에도 컴파일 타임에도 명시적으로 이 객체는 동결 되었다는 것을 알 수 있게 되었습니다.

0개의 댓글