Я хочу сопоставить объединение массивов, как я могу безопасно ввести его?
У меня есть решение, но я хочу избежать жесткого кодирования типов из объединения типов.
При вычислении newArr1
, как и ожидалось, получаю ошибку: Property 'field2' does not exist on type 'Common<A, B>'
Есть ли способ получить ту же ошибку при вычислении newArr2
?
interface A {
field1: string
field2: number
}
interface B {
field1: string
}
type Common<A, B> = {
[P in keyof A & keyof B]: A[P] | B[P];
}
function mapArray(arr: A[] | B[]) {
const newArr1 = (arr as Common<A, B>[]).map(i => i.field2) // error
// how can I get the same error without hard-coding the types from the type union?
const newArr2 = (arr as MagicEventuallyFromJcalz<typeof arr>).map(i => i.field2)
return newArr1
}
Кроме того, разве второй не будет чем-то вроде (arr as Common<typeof arr[number]>[])
? Или вы ожидаете, что Common<A[] | B[]>
каким-то образом будет таким же, как Common<A | B>[]
? Кажется, что нераспределяемый «массив» трудно получить бесплатно; это может быть жестко закодировано, но я думаю, что хотел бы увидеть более полный вариант использования
И мой первый набросок Common<T>
без массивности будет type Common<T> = Pick<T, keyof T>
.
Почему бы и нет mapArray(arr: (A | B)[])
? mapArray(arr: A[] | B[])
говорит, что у вас либо есть массив всех A, либо все B, это объединение типов массивов, а не типов элементов, которые есть в массиве, поэтому было бы сложно без двух путей кода для каждого.
Спасибо jcalz и Уильяму Лохану!
Реальный пример использования: на 2 разных запроса от внешнего API я получаю 2 разных ответа:
Используя ту же функцию, которая анализирует один из двух массивов сверху, я хочу получить идентификаторы пользователей (id
является общим свойством пользователей из обоих массивов), 1) безопасным способом и 2) без жесткого кодирования типы массивов в теле функции. Основываясь на ваших комментариях jcalz, я смог этого добиться. Вы заслуживаете похвалы за решение, если вы опубликуете ответ, я отмечу его как принятый.
Решение, которое я нашел (ссылка на код):
interface DetailedUser {
id: number
many: number
other: string
fields: boolean
}
interface CompactUser {
id: number
}
type Common<T> = Pick<T, keyof T>
type UnpackedFromArray<T> = T extends (infer U)[] ? U : never
function getUserIdsFromApi(usersJsonFromApi: DetailedUser[] | CompactUser[]) {
// works as expected: in both lines below, a ts error is raised
// note that the types (DetailedUser[], CompactUser[]) are not hard-coded, but determined from variable usersJsonFromApi
const userIds1 = (usersJsonFromApi as Common<UnpackedFromArray<typeof usersJsonFromApi>>[]).map(i => i.other)
const userIds2 = (usersJsonFromApi as Common<typeof usersJsonFromApi[number]>[]).map(i => i.other)
return userIds1
}
Обычно я предпочитаю type UnpackedFromArray<T extends Array<any>> = T[number]
(не то чтобы вам нужно было давать ему имя для такой короткой вещи) условному типу T extends (infer U)[] ? U : never
, поскольку первый, тип поиска, компилятору легче понять. Но они оба работают.
Профсоюзы уже действуют таким образом. Проблема в том, что объединения функций трудно вызвать. У вас есть вариант использования лучше, чем
mapArray()
? Потому что я бы просто использовалarr as (A|B)[]
здесь, не делая сумасшествия.