Item 23 한꺼번에 객체 생성하기

객체는 한번에 만들자

Item 20 다른 타입에는 다른 변수 사용하기에서 설명했듯이 변수의 값은 변경될 수 있지만, 타입스크립트의 타입은 일반적으로 변경되지 않는다.

객체를 생성할 땐 속성을 하나씩 추가하기보다는 여러 속성을 포함해서 한꺼번에 생성해야 타입 추론에 유리하다.

const pt = {}
pt.x = 3
// ~ '{}' 형식에 'x' 속성이 없습니다.
pt.y = 4
// ~ '{}' 형식에 'y' 속성이 없습니다.

// 위보다는 아래를 선호
const pt = {
  x: 3,
  y: 4,
}
  • as 문을 이용해 타입 단언으로 우회할 수는 있지만 좋은 방법 같진 않다.

객체 전개 연산자 (Spread Operator) (...)

객체 전개 연산자를 이용하면 큰 객체를 한꺼번에 만들어 낼 수 있다.

const namedPoint = { ...pt, ...id }
namedPoint.name // OK, 타입은 string
  • 객체 전개 연산자를 이용하면 타입 걱정 없이 필드 단위로 객체를 생성할 수 있다. 이때 모든 업데이트마다 새 변수를 사용해 각각 새로운 타입을 얻도록 하는 게 중요하다.

조건부 속성 만들기

타입에 안전한 방식으로 조건부 속성을 추가하려면, 속성을 추가하지 않는 null 또는 {}로 객체 전개를 사용하면 된다.

declare let hasMiddle: boolean
const firstLast = { first: 'Harry', last: 'Truman' }
const president = { ...firstLast, ...(hasMiddle ? { middle: 'S' } : {}) }

추론은 다음과 같이 된다.

const president: {
  middle?: string
  first: string
  last: string
}

전개 연산자로 한꺼번에 여러 속성을 추가할 수도 있다.

declare let hasDates: boolean
const nameTitle = { name: 'Khufu', title: 'Pharaoh' }
const pharaoh = {
  ...nameTitle,
  ...(hasDates ? { start: -2589, end: -2566 } : {}),
}

타입 추론은 다음과 같이 유니온으로 표현된다.

const pharaoh: {
  start: number
  end: number
  name: string
  title: string
} | {
  name: string
  title: string
}

startend가 선택적 필드로 나타나길 바랬다면 당황스러울 수 있다. 이 타입에서는 start를 읽을 수 없다.

유니온보다는 선택적 필드가 다루기에는 더 쉬울 수 있는데, 선택적 필드 방식으로 표현하려면 다음과 같이 헬퍼 함수를 사용하자.

function addOptional<T extends object, U extends object>(
  a: T, b: U | null
): T & Partial<U> {
  return { ...a, ...b }
}

const pharaoh = addOptional(
  nameTitle,
  hasDates ? { start: -2589, end: -2566 } : null
)
pharaoh.start // OK, 타입은 number | undefined

가끔 객체나 배열을 변환해 새로운 객체나 배열을 생성하고 싶을 수 있는데, 이런 경우 루프 대신 내장된 함수형 기법 또는 로대시(Lodash) 같은 유틸리티 라이브러리를 사용하는 것이 '한꺼번에 객체 생성하기' 관점에서 보면 더 옳다. Item 27에서 다룬다.

Summary

  • 속성을 제각각 추가하지 말고 한꺼번에 객체로 만들자. 안전한 타입으로 속성을 추가하려면 객체 전개({ ...a, ...b })를 사용하면 된다.

  • 객체에 조건부로 속성을 추가하는 방법을 익히자.

Last updated