Машинопись: Как я могу делать записи в карте ES6 на основе типа объединения - PullRequest
0 голосов
/ 27 февраля 2019

Я хотел бы использовать карту вместо карты объектов для объявления некоторых ключей и значений.Но Typescript, похоже, не поддерживает типы индексов для карты ES6, это правильно и есть какие-то обходные пути?

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

Вот некоторый псевдокод, описывающий то, чего я пытаюсь достичь:

type Keys = 'key1' | 'key2';

type  Values = {
  'key1': string;
  'key2': number;
}

/** Should display missing entry error */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'error missing key'],
]);

/** Should display wrong value type error for 'key2' */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'okay'],
  ['key2', 'error: this value should be number'],
]);

/** Should pass */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'all good'],
  ['key2', 42],
]);

Редактировать: больше кода, который частично описывает мойслучай использования

enum Types = {
  ADD = 'ADD',
  REMOVE = 'REMOVE',
};

/** I would like type-safety and autocompletion for the payload parameter */
const handleAdd = (state, payload) => ({...state, payload});

/** I would like to ensure that all types declared in Types are implemented */
export const reducers = new Map([
  [Types.ADD, handleAdd],
  [Types.REMOVE, handleRemove]
]);

1 Ответ

0 голосов
/ 27 февраля 2019

Вот самое близкое, что я могу себе представить, хотя я все еще не понимаю, почему мы не просто используем простые объекты для начала:

type ObjectToEntries<O extends object> = { [K in keyof O]: [K, O[K]] }[keyof O]

interface ObjectMap<O extends object> {
  forEach(callbackfn: <K extends keyof O>(
    value: O[K], key: K, map: ObjectMap<O>
  ) => void, thisArg?: any): void;
  get<K extends keyof O>(key: K): O[K];
  set<K extends keyof O>(key: K, value: O[K]): this;
  readonly size: number;
  [Symbol.iterator](): IterableIterator<ObjectToEntries<O>>;
  entries(): IterableIterator<ObjectToEntries<O>>;
  keys(): IterableIterator<keyof O>;
  values(): IterableIterator<O[keyof O]>;
  readonly [Symbol.toStringTag]: string;
}

interface ObjectMapConstructor {
  new <E extends Array<[K, any]>, K extends keyof any>(
    entries: E
  ): ObjectMap<{ [P in E[0][0]]: Extract<E[number], [P, any]>[1] }>;
  readonly prototype: ObjectMap<any>;
}

const ObjectMap = Map as ObjectMapConstructor;

Идея состоит в том, чтобы создать новый интерфейс, ObjectMap, который конкретно зависит от типа объекта O для определения его отношения ключ / значение.И тогда вы можете сказать, что конструктор Map может действовать как конструктор ObjectMap.Я также удалил все методы, которые могут изменить, какие ключи на самом деле присутствуют (и метод has() также избыточно true).

Я могу разобраться с каждым методом и определением свойства, но этомного жонглирования.Короче говоря, вы хотите использовать K extends keyof O и O[K] для представления типов, обычно представляемых K и V в Map<K, V>.

Конструктор немного раздражает в том, что вывод типов работает не так, как вам хотелось бы, поэтому обеспечение безопасности типов происходит в два этапа:

// let the compiler infer the type returned by the constructor
const myMapInferredType = new ObjectMap([
  ['key1', 'v'], 
  ['key2', 1],  
]);

// make sure it's assignable to `ObjectMap<Values>`: 
const myMap: ObjectMap<Values> = myMapInferredType;

Если ваш myMapInferredType не соответствует ObjectMap<Values> (например, вам не хватает ключей или неправильных типов значений), тогда myMap выдаст вам ошибки.

Теперь вы можете использовать myMap как ObjectMap<Values>,аналогично тому, как вы используете Map экземпляр с get() и set(), и он должен быть безопасным для типа.

Пожалуйста, обратите внимание еще раз ... это похоже на большую работу дляболее сложный объект с более сложными типами и не более функциональными, чем простой объект.Я бы серьезно предупредил любого, кто использует Map, чьи ключи являются подтипами keyof any (то есть string | number | symbol), чтобы настоятельно рассмотреть возможность использования простого объекта вместо этого, и был бы уверен, что ваш вариант использования действительно требуетMap.

Хорошо, надеюсь, это поможет вам.Удачи!

...