#typescript
Вопрос:
Допустим, у меня есть такой объект, как этот:
const person = {
id: 87
name: 'Some Name',
address: {
street: 'Some street',
country: 'Some country'
}
}
Я хочу получить тип, который представляет собой объединение всех пар ключевых значений. Таким образом, тип должен быть:
{ id: number } | { name: string } | { address: { street: string; country: string; } }
Как это можно сделать? Я попробовал это:
type PersonInfo = {
id: number;
name: string;
address: {
street: string;
country: string;
}
}
type PersonMap<M extends { [index: string]: any }> = {
[Key in keyof M]: M[Key]
};
type PersonTest1 = PersonMap<PersonInfo>[keyof PersonMap<PersonInfo>];
// Returns "number | string | { street: string; country: string; }"
type PersonTest2 = PersonMap<PersonInfo>;
// Returns { id: number; name: string; address: { street: string; country: string;} }
Как я могу получить тип объединения, описанный выше?
Комментарии:
1. почему бы вам просто не определить типы отдельно, а затем объединить их, чтобы создать свой personinfo?
2. @DanielA. Если бы я мог это сделать, мне просто было интересно, есть ли какой-нибудь короткий путь. Приведенный выше пример является всего лишь примером — на самом деле у меня действительно большой объект с множеством пар ключ-значение, и я хотел бы легко получить тип объединения всех этих пар.
3. Это возможно с помощью записей массива. Судя по тому, что я могу сказать, это невозможно с отдельными объектами k/v.
Ответ №1:
Похоже, вам нужна функция типа формы UnionOfSingleKeyObjects<T>
, которая превращает тип объекта T
в объединение типов объектов с одним ключом, где каждый keyof T
вход появляется ровно один раз. В зависимости от ваших вариантов использования вы можете определить функцию такого типа следующим образом:
type UnionOfSingleKeyObjects<T extends object> =
{ [K in keyof T]-?: { [P in K]: T[P] } }[keyof T]
И убедитесь, что он работает так PersonInfo
, как требуется:
type PersonKVPairs = UnionOfSingleKeyObjects<PersonInfo>
/* type PersonKVPairs = {
id: number;
} | {
name: string;
} | {
address: {
street: string;
country: string;
};
} */
Определение UnionOfSingleKeyObjects
-это сопоставленный тип , в котором мы перебираем каждый тип ключа K
keyof T
, вычисляем рассматриваемый объект с одним ключом для каждого ключа, а затем индексируем его, keyof T
чтобы получить объединение всех типов объектов с одним ключом.
Вместо индексации в сопоставленный тип вы можете использовать распределительные условные типы, чтобы получить тот же эффект:
type UnionOfSingleKeyObjects<T extends object> =
keyof T extends infer K ? K extends keyof T ?
{ [P in K]: T[P] } : never : never
В любом случае работает; Я склонен использовать сопоставленные типы, потому что их немного легче объяснить, чем распределение условных типов.
В обоих этих подходах объект с одним ключом с ключом K
записывается как {[P in K]: T[P] }
. В качестве альтернативы это может быть записано как Record<K, T[K]>
с использованием Record<K, V>
типа утилиты или как Pick<T, K>
с использованием Pick<T, K>
типа утилиты. У этих других версий есть свои плюсы и минусы, и они могут изменить способ отображения типа в IntelliSense quickinfo, а также то, остаются ли необязательные/ readonly
ключи необязательными/ readonly
в выводе. Если вы заботитесь о сохранении этих модификаторов и не хотите видеть Pick
или Record
в своей быстрой информации, вы можете написать это примерно {[P in keyof Pick<T, K>]: T[P]}
так:
type UnionOfSingleKeyObjects<T extends object> =
{ [K in keyof T]-?: { [P in keyof Pick<T, K>]: T[P] } }[keyof T]
и мы видим, что такие модификаторы сохраняются:
type Example = UnionOfSingleKeyObjects<{ a?: string, readonly b: number }>
/* type Example = {
a?: string;
} | {
readonly b: number;
} */
Опять же, в зависимости от ваших вариантов использования, вас могут волновать или не волновать такие вещи.
Комментарии:
1. Почему [P в K] требуется в типе UnionOfSingleKeyObjects<T расширяет объект> = { [K в ключе T]-?: { [P в K]: T[P] } }[ключ T] ? Т. е. поскольку K — это всего лишь один ключ-почему мы не можем просто сделать { [K]: T[K] }? Я знаю, что это приводит к ошибке, но я не понимаю, почему, поскольку P и K, похоже, одно и то же.
2. Ну, как вы сказали, это выдает ошибку; синтаксис вычисляемого свойства
{[k]: YYY}
работает только в том случае, еслиk
это значение , а не тип (и, кроме того, типk
должен быть буквальным или уникальным символом). Мы используем сопоставленные типы, а не вычисляемые свойства. Синтаксис для сопоставленных типов-{[P in XXX]: YYY}
XXX
это тип ключа или объединение типов ключей, иYYY
это тип, от которого, возможно, зависитP
. Даже если у вас есть только один ключ, вы должны написать[P in K]
, чтобы он работал.