Как создать тип, содержащий только общие свойства из объединения типов?

Я хочу сопоставить объединение массивов, как я могу безопасно ввести его?

У меня есть решение, но я хочу избежать жесткого кодирования типов из объединения типов. При вычислении 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
}

Профсоюзы уже действуют таким образом. Проблема в том, что объединения функций трудно вызвать. У вас есть вариант использования лучше, чем mapArray()? Потому что я бы просто использовал arr as (A|B)[] здесь, не делая сумасшествия.

jcalz 29.05.2019 20:51

Кроме того, разве второй не будет чем-то вроде (arr as Common<typeof arr[number]>[])? Или вы ожидаете, что Common<A[] | B[]> каким-то образом будет таким же, как Common<A | B>[]? Кажется, что нераспределяемый «массив» трудно получить бесплатно; это может быть жестко закодировано, но я думаю, что хотел бы увидеть более полный вариант использования

jcalz 29.05.2019 20:58

И мой первый набросок Common<T> без массивности будет type Common<T> = Pick<T, keyof T>.

jcalz 29.05.2019 20:59

Почему бы и нет mapArray(arr: (A | B)[])? mapArray(arr: A[] | B[]) говорит, что у вас либо есть массив всех A, либо все B, это объединение типов массивов, а не типов элементов, которые есть в массиве, поэтому было бы сложно без двух путей кода для каждого.

William Lohan 29.05.2019 21:45
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
4
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо jcalz и Уильяму Лохану!

Реальный пример использования: на 2 разных запроса от внешнего API я получаю 2 разных ответа:

  1. массив с пользователями, имеющими много деталей о каждом пользователе, и
  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, поскольку первый, тип поиска, компилятору легче понять. Но они оба работают.

jcalz 30.05.2019 02:20

Другие вопросы по теме