#typescript #typescript-generics
Вопрос:
Допустим, у нас есть тип
type Foo = {
a: string;
b: number;
c: boolean;
}
Теперь я хочу определить тип для объекта , имеющего a key
и a value
заданного типа T
, чтобы тип значения выводился непосредственно из ключа, чтобы я мог сделать:
const kv1: KeyValue<Foo> = {key: 'a', value: 'STRING'};
const kv2: KeyValue<Foo> = {key: 'b', value: 42};
const kv3: KeyValue<Foo> = {key: 'c', value: true};
Если я просто сделаю это:
type KeyValue<T> = {
key: keyof T;
value: T[keyof T]
}
… тогда, очевидно, значения будут представлять собой объединение значений всех свойств в Foo
:
И если я сделаю это:
type KeyValue<T, K extends keyof T> = {
key: K;
value: T[K]
}
… тогда конечно, если явно набирается как KeyValue<Foo, 'a'>
я могу создать объект литералы, которые соответствуют типу Foo
:s свойства, а если я не конкретно указать тип и ключ для каждого литерала, а просто делать KeyValue<Foo, keyof Foo>
, каждое значение будет позволено иметь тип любого значения Foo
, то есть, все значения будут string | number | boolean
вместо того, чтобы вывести из введенных key
.
В конце концов, я хочу иметь возможность делать такие вещи, как это:
const kvs: Array<KeyValue<Foo>> = [
{key: 'a', value: 'STRING'}, // should not compile if value is a number or boolean
{key: 'b', value: 42}, // should not compile if value is a string or boolean
{key: 'c', value: true}, // should not compile if value is a string or number
];
Можно ли вывести KeyValue
тип с этими ограничениями, чтобы он эффективно стал KeyValue<Foo, 'a'> | KeyValue<Foo, 'b'> | KeyValue<Foo, 'c'>
во втором примере выше, без необходимости вручную писать это объединение? В принципе, я предполагаю, что я хотел бы иметь возможность определить тип value
из значения key
в текущем литеральном объекте.
Ответ №1:
Вы не можете представлять каждую пару ключ-значение с помощью одного KeyValue
объекта, так как это потребовало бы либо частичного вывода (для ключа), либо какой-либо ссылки на key
свойство из value
свойства внутри одного и того же объекта.
Однако то, что вы могли бы сделать, — это создать дискриминируемое объединение с использованием объекта ввода.
type Foo = {
a: string;
b: number;
c: boolean;
}
type KeyValue<T> = {
[P in keyof T]: {
key: P;
value: T[P];
}
}[keyof T];
// with KeyValue<Foo>, this generates the following type
//
// {
// key: "a";
// value: string;
// } | {
// key: "b";
// value: number;
// } | {
// key: "c";
// value: boolean;
// }
const kvs: Array<KeyValue<Foo>> = [
{key: 'a', value: 40}, // ERROR
{key: 'b', value: true}, // ERROR
{key: 'c', value: "foo"}, // ERROR
];
Комментарии:
1. Черт, вот это умно! Слава!