Типизируем значения массива
Коллеги привет.
Есть такой тип
type SuperPuperType = {
v1: number
v2: string
}
И есть массив объектов состоящих из двух полей:
- name (в котором может быть 'v1' или 'v2')
- value в котором хочется или number или string в зависимости от того, какое значение у name.
Вот с дженериком для этого массива возникли проблемы. Подскажите плиз. Спасибо!
UPD Видимо, не до конца понятно объяснил.
Вот такое должно проходить
const SuperArray: SuperPuperGeneric<SuperPuperType>[] = [{
name: 'v1'
value: 1
},{
name: 'v2'
value: 'fooBoo'
}]
А вот такие варианты нет
const SuperArray: SuperPuperGeneric<SuperPuperType>[] = [{
name: 'v1'
value: 1
},{
name: 'v2'
value: 2
}]
const SuperArray2: SuperPuperGeneric<SuperPuperType>[] = [{
name: 'v3'
value: 1
},{
name: 'v2'
value: 'fooBoo'
}]
Ответы (3 шт):
Автор решения: smellyshovel
→ Ссылка
Как вариант:
type SuperPuperType = {
v1: number
v2: string
}
type OneOfArray<T extends keyof SuperPuperType> = {
name: SuperPuperType[T]
value: SuperPuperType[T]
}
// массив из объектов OneOfArray, параметр которого принимает один из полей SuperPuperType'а
const a: OneOfArray<"v1">[] = [{
name: 2,
value: 2 // если сюда вставить строку, то будет ошибка
}]
Автор решения: EzioMercer
→ Ссылка
Можно так:
type SuperPuperType = {
name: 'v1',
value: number
} | {
name: 'v2',
value: string
};
//OK
const SuperArray: SuperPuperType[] = [{
name: 'v1',
value: 1
},{
name: 'v2',
value: 'fooBoo'
}]
//Error 1
const SuperArray1: SuperPuperType[] = [{
name: 'v1',
value: 1
},{
name: 'v2',
value: 2
}]
//Error 2
const SuperArray2: SuperPuperType[] = [{
name: 'v3',
value: 1
},{
name: 'v2',
value: 'fooBoo'
}]
Errors:
-
Type '{ name: "v2"; value: number; }' is not assignable to type 'SuperPuperType'.
Types of property 'value' are incompatible.
Type 'number' is not assignable to type 'string'.
-
Type '"v3"' is not assignable to type '"v1" | "v2"'.
Автор решения: sailybra
→ Ссылка
Вариант как у EzioMercer, но тип создаётся автоматически:
// Задаём мапинг { [значение name]: тип value }
type MyMapping = {
v1: number
v2: string
};
// Создаём юнион объектов из мапинга
type MyObject<Mapping> = keyof Mapping extends infer Key
? Key extends keyof Mapping
? {
name: Key,
value: Mapping[Key]
}
: never
: never;
//OK
const test1: MyObject<MyMapping>[] = [{
name: 'v1',
value: 1
},{
name: 'v2',
value: 'fooBoo'
}];
//Error 1
const test2: MyObject<MyMapping>[] = [{
name: 'v1',
value: 1
},{
name: 'v2',
value: 2
}];
//Error 2
const test3: MyObject<MyMapping>[] = [{
name: 'v3',
value: 1
},{
name: 'v2',
value: 'fooBoo'
}];
Как это работает?
- Сначала мы берём юнион всех ключей типа
MyMappingи выделяем его в параметрKey, он нужен чтобы можно было преобразовывать этот тип далее. - Проходим по всем элементам юниона и преобразовываем их в объекты. Здесь используется свойство условных типов распределяться по юниону, то есть
T<A | B>будет вычислено какT<A> | T<B>. Подробнее об этом можно прочитать в документации. ВнутриextendsтипKeyсоответствует каждому отдельному элементу юниона.