#typescript #typescript-generics #typing
#typescript #typescript-generics #ввод
Вопрос:
Я хотел бы сравнить и назначить определенные поля двух объектов, скажем:
const left = { a: 1, ignored: 5, something: 7 };
const right = { a: 2, ignored: 6, else: 8, id: 18 };
И я хочу вызвать leftToRight(left, right, ['a'])
, после чего right
должно быть:
{ a: 1, ignored: 6, id: 18 }
и я хочу вызвать какую-то другую функцию и передать right.id , который, как я знаю, существует во втором аргументе.
Мой текущий подход:
leftToRight(left, right, keys) {
let changed = false;
for (const key of keys) {
if (!Object.is(left[key], right[key])) {
right[key] = left[key];
changed = true;
}
}
doSomething(right.id)
return changed
}
Я изо всех сил пытаюсь найти подходящее определение типа:-(
Первоначальный подход:
leftToRight<T>(left: T, right: T, keys: Array<keyof T>): boolean
приводит к: «Свойство ‘id’ не существует для типа ‘T'», и я не нашел способа проверить это ( 'id' in right
)
Вторая попытка:
leftToRight<T>(left: T, right: T amp; {id: number}, keys: Array<keyof T>): boolean
приводит к тому, что «Тип ‘T’ не может быть присвоен типу ‘{ id: number; }'» для right[key] = left[key]
Третья попытка:
leftToRight<T, U extends {id: number}>(left: T, right: U, keys: Array<keyof T amp; keyof U>): boolean
снова приводит к ошибке присвоения right[key] = left[key]
, поскольку типы T и U могут быть совершенно не связаны.
Комментарии:
1.
id
Должно ли поле быть astring
или anumber
?2. Я исправил вопрос. Это число.
3. Я бы использовал
leftToRight<T>(left: T, right: T amp; {id: number}, keys: Array<keyof T>): boolean
и внутри реализации do(right as T)[key] = left[key]
или что-то подобное, чтобы избавиться от этой ошибки; Я ищу, почему у компилятора возникла проблема с присвоениемT[keyof T]
(T amp; {id: number})[keyof T])
, но я пока ничего не нашел.
Ответ №1:
Редактировать:
Вы уточнили свои требования, чтобы сказать, что Left
и Right
у каждого могут быть свойства, которых нет в другом. Для обработки этого сценария нам нужны два общих типа.
Right
это объект, который включает в себя id
. Мы хотим, чтобы выбранные ключи присутствовали в обоих Left
, Right
и мы хотим, чтобы они имели одинаковые типы значений. Я определил generic Keys
как любое подмножество ключей Right
. Я определил Left
как подмножество Right
, содержащее все эти ключи.
function leftToRight<Right extends {id: number}, Keys extends keyof Right>(
left: Pick<Right, Keys>, right: Right, keys: Keys[]
) {
let changed = false;
for (const key of keys) {
if (!Object.is(left[key], right[key])) {
right[key] = left[key];
changed = true;
}
}
doSomething(right.id)
return changed
}
Оригинальный ответ:
Я попробовал несколько вещей, прежде чем нашел тот, который работает. Конечно, это не самое чистое определение. Я получил ошибки при использовании generic для описания Left
и добавления id
в get Right
, но удаления из Right
works.
Мы говорим, что Right
это объект со {id: number}
свойством и Left
должен иметь все свойства Right
, кроме for id
. Для того, чтобы элементы of keys
присутствовали в обоих объектах, нам нужно игнорировать ключ id
from keyof Right
.
function leftToRight<Right extends {id: number}>(
left: Omit<Right, 'id'>, right: Right, keys: (Exclude<keyof Right, 'id'>)[]
) {
let changed = false;
for (const key of keys) {
if (!Object.is(left[key], right[key])) {
right[key] = left[key];
changed = true;
}
}
doSomething(right.id)
return changed
}
Комментарии:
1. Спасибо за ваше решение. Я не был достаточно ясен в своем вопросе (добавлю его):
left
не обязательно обладает всеми свойствамиright
(и наоборот).2. Хорошо, так
right
что всегда имеетid
,left
иright
имеет какие-либо свойства, иkeys
должны быть только общие свойства, это правильно? Нам понадобятся (как минимум) два обобщения, но я думаю, что смогу это сделать. Я посмотрю…
Ответ №2:
Попробуйте с left: Partial<T>
помощью и подтвердите, что left[key]
это не определено с !
помощью .
const left = { a: 1, ignored: 5};
const right = { a: 2, ignored: 6, id: 18 };
function leftToRight<T>(left: Partial<T>, right: T, keys: Array<keyof T>) {
let changed = false;
for (const key of keys) {
if (!Object.is(left[key], right[key])) {
right[key] = left[key]!;
changed = true;
}
}
// doSomething(right.id)
return changed
}
Комментарии:
1. Похоже, это сработает, если я использую:
function leftToRight<T extends {id: number}>(left: Partial<T>, right: T, keys: Array<keyof T>)
. Есть идеи, как избежать ненулевого утверждения?2. Не уверен, каковы ваши мотивы для избежания ненулевого утверждения, поэтому я не знаю, лучше ли это, но вы могли бы использовать
left
asT
. Пример:right[key] = (left as T)[key]
3. Я бы хотел избежать жалоб от моего linter 😉 и ситуаций, когда во время выполнения обнаруживаются ошибки, которые могли быть обнаружены во время компиляции (в режиме строгой проверки нуля). В этом случае
left[key]
это не определено, и, как следствие, right [key] будет неопределенным, что может привести к проблемам, если последующий код этого не ожидает. Однако это, вероятно, академическая проблема. Итак, спасибо за ваш ответ. Он делает то, что должен 🙂