Общие данные по ключу объекта

Это код, который у меня есть:

type Bucket = {
  [key:string]:Map<string,{data:any}>
}

Теперь я пытаюсь создать type, который позволил бы мне иметь custom данные по ключам. Как вы можете видеть в приведенном ниже коде, я бы очень хотел ввести объект data для каждого ключа. Я пробовал использовать общий объект типа Bucket, но он блокирует все ключи для одних и тех же данных.

Есть ли способ иметь разные строго типизированные data для каждого ключа?

const bucket:Bucket = {
  a:new Map(),
  b:new Map<string,{data:boolean}>()
}

bucket.a.set('a',{data:1})
bucket.b.set('b',{data:'b'}) // this should error

const getA = bucket.a.get('a') // data is any - should be inferred to number?
const getB = bucket.b.get('b') // data is any - should be boolean

TS площадка


person Ivan V.    schedule 10.02.2021    source источник
comment
С функцией идентичности typescriptlang.org/ play? # code / Вам все равно нужно будет явно ввести a   -  person Aleksey L.    schedule 10.02.2021
comment
Готов поспорить на 10 долларов, что одна из этих ссылок может вам помочь stackoverflow.com/questions/65891135/ и stackoverflow.com/questions/65978153/   -  person captain-yossarian    schedule 10.02.2021


Ответы (1)


Когда вы определяете тип Bucket с индексированной подписью ({ [x: <key type>] : <value type> }), вы сообщаете компилятору, что нет прямой корреляции между конкретными элементами и их типами. Кроме того, any не является типом заполнителя, он отключается проверки типов. Таким образом, компилятор показывает вам то, что вы ему сказали, а именно:

  1. все ключи bucket относятся к типу string
  2. bucket имеет значения типа Map<string, { data: any }.

Затем вы назначаете объект переменной bucket. На этом этапе значение проверяется на совместимость с предоставленным типом (Bucket), и, поскольку вы отключили проверку типа для data члена, any все идет, поэтому ошибок нет.

Затем, когда вы get значение члена bucket, тип определяется как { data: any; } | undefined, потому что нет гарантии, что "a" или "b" будут присутствовать в [x: string], следовательно, значение может быть undefined. Мы уже говорили о том, почему data не сужается.

Если вы удалите аннотацию типа из bucket, вы заметите, что вывод резко улучшится, потому что теперь также выводится тип: { a: Map<any, any>; b: Map<string, { data: boolean; }>. В то же время вы теряете проверку типов, какие элементы bucket могут содержать.

Итак, что вы можете с этим поделать? Вам необходимо явно указать компилятору, какова взаимосвязь между определенными ключами и значениями. Создание общего Bucket может помочь нам в этом:

type Bucket<T> = {
  [ P in keyof T ]: Map<string, { data: T[P] }>
}

Теперь вы правильно вывели data типов, уверенность в форме bucket и автозаполнение за счет большей подробности, что является небольшой платой за эти преимущества (кроме того, вы можете сделать параметр типа интерфейсом или типом):

type Bucket<T> = {
  [ P in keyof T ]: Map<string, { data: T[P] }>
}

const bucket: Bucket<{ a: number, b: boolean, c?: string }> = {
  a:new Map(),
  b:new Map(),
};

bucket.a.set('a', { data: 1 })
bucket.b.set('b', { data: 'b' }) // Type 'string' is not assignable to type 'boolean'

const getA = bucket.a.get('a') // data is number
const getB = bucket.b.get('b') // data is boolean

площадка


Функция идентификации, подобная упомянутой Алексеем Л., делает то же самое - удаляет явную аннотацию типа из bucket, сохраняя при этом проверку типа (ограничение extends Bucket) и возвращая буквальный тип (форма <T>(a:T) => T). Он требует создания функции в скомпилированном коде, но позволяет вам определить тип bucket.

person Oleg Valter    schedule 11.02.2021
comment
@IvanV. NP-сопоставленные типы с параметрами универсального типа - это первое, что приходит на ум, когда мне нужно сопоставить ключи и значения (хотя мне также нравится идея использования функции идентификации) - person Oleg Valter; 14.02.2021