Условно и встроенно добавляет свойство к объекту типобезопасным способом

#typescript

#typescript

Вопрос:

Цель

Цель состоит в том, чтобы создать объект при условном добавлении свойств. Применяются следующие правила:

  1. не допускается, чтобы в конечном итоге для необязательного свойства было установлено значение undefined .
  2. это должно быть сделано встроенным (например, без циклов / .reduce и т. Д. Для создания объекта)

Два сценария

Рассмотрим следующий код:

 interface User {
  readonly firstName: string
  readonly lastName: string
}
interface ParitalUser {
  readonly firstName?: string
  readonly lastName?: string
}

const updateLastName = false

// SCENARIO 1
// create a user and conditionally add a name property
const partialUserA: ParitalUser = {
  ...(updateLastName ? { lastName: 'Doe' } : {}),
}

// i want the following assertion pass (so 'userA' should not have a property 'name' if addName is false)
console.assert(!('lastName' in partialUserA))

// but this is not type safe:
const partialUserB: ParitalUser = {
  ...(Math.random() < 0.5 ? { lastName: 'Doe' } : {}),
  ...(Math.random() < 0.5 ? { age: 21 } : {}), // should be an error, but isn't!
}

// SCENARIO 2
// i know I can do this
const partialUserC: ParitalUser = {
  lastName: updateLastName ? 'Doe' : undefined,
}

// this is type safe:
const partialUserB: ParitalUser = {
  lastName: updateLastName ? 'Doe' : undefined,
  age: Math.random() < 0.5 ? 21 : undefined, // is an error (as it should be) 
}

// but the this assertion doesn't pass anymore
console.assert(!('lastName' in partialUserC))
  

(игровая площадка, с которой можно поиграть, находится здесь)

Проблема

  • SCENARIO 1 приводит к объекту без свойства lastName , но он не является типобезопасным
  • SCENARIO 2 является типобезопасным, но приводит к объекту с неопределенным свойством lastName (см. Правило 1)

Но почему?

Рассмотрим следующий код:

 const userToUpdate: User = {
  firstName: 'Frank',
  lastName: 'Smith',
}

const userA: User = {
  ...userToUpdate,
  ...partialUserA,
}
// becomes { firstName: 'Frank', lastName: 'Smith' }

const userC: User = {
  ...userToUpdate,
  ...partialUserC,
}
// becomes { firstName: 'Frank', lastName: undefined }
  

(игровая площадка, с которой можно поиграть, находится здесь)

Предполагаемое использование всего этого — создавать частичные объекты, которые представляют свойства, которые необходимо обновить.

userC , который был «обновлен» с использованием объекта, содержащего undefined свойство, неправильно перезаписывает это свойство. userA создается правильно с использованием partialUserA , но это не было (см. Выше) типобезопасной операцией.