Введите безопасное слияние литералов объекта в машинописи - PullRequest
0 голосов
/ 10 января 2020

Я хочу объединить два объекта машинописи (используя разброс объектов):

var one = { a: 1 }
var two = { a: 2, b: 3 }
var m = {...one, ...two} // problem as property `a` is overwritten

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

type UniqueObject<T extends {[K in keyof U]?: any}, U> =
    {[K in keyof U]: T[K] extends U[K] ? never : U[K]}

var one = { a: 1 }
var two1 = { a: 2, b: 3 }
var two1_: UniqueObject<typeof one, typeof two1> = two1 // errors correctly
var two2 = { a: undefined, b: 1 }
var two2_: UniqueObject<typeof one, typeof two2> = two2 // passes incorrectly

Другая версия года go, которая, как я думал, работала в то время, имела undefined extends U[K] на месте из T[K] extends U[K]:

type UniqueObject<T extends {[K in keyof U]?: any}, U> =
    {[K in keyof U]: undefined extends T[K] ? U[K]: never}

Ни одна из этих двух работ. Я подозреваю, что это потому, что оба undefined extends U[K] или T[K] extends U[K] имеют значение false, поскольку свойство K в T является необязательным. Не уверен, как или можно ли обойти это.

1 Ответ

1 голос
/ 10 января 2020

Обе ваши версии более или менее эквивалентны - переключаются только ветви истина / ложь в условном типе.

Ограничение T extends {[K in keyof U]?: any} немного проблематично c: при удалении a в two возникает ошибка Type '{ a: number; }' has no properties in common with type '{ b?: any; }, что на самом деле должно быть успехом.

Также следует помнить, что результирующий тип merge не содержит определения объединенного типа из обоих типов , Мы можем изменить объявление следующим образом:

type UniqueObject<T, U> =
    T & { [K in keyof U]: K extends keyof T ? never : U[K] }

Теперь компилятор корректно выдает ошибку с дублирующим свойством a:

var one = { a: 1 }
var two = { a: 2, b: 3 }
//                                                        v a becomes never here
type Merge = UniqueObject<typeof one, typeof two> // { a: never; b: number; }
const res: Merge = { ...one, ...two } // errors now, cannot assign number to never

В следующем я немного упростил тип и все упаковано в компактную вспомогательную функцию для управления типами + оператор распространения:

function mergeUnique<T extends object, U extends object & { [K in keyof U]: K extends keyof T ? never : U[K] }>(o1: T, o2: U) {
    return { ...o1, ...o2 }
}

const res21 = mergeUnique({ a: 1 }, { b: 3 })
const res22 = mergeUnique({ a: 1 }, { a: 2, b: 3 }) // error
const res23 = mergeUnique({ a: 1, c: 5 }, { b: 3 })
const res24 = mergeUnique({ a: 1}, { a: undefined }) // error

Пример кода

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...