打字稿:如何根据对象键/值类型在 ES6 Map 中创建条目 [英] Typescript: How can I make entries in an ES6 Map based on an object key/value type

查看:24
本文介绍了打字稿:如何根据对象键/值类型在 ES6 Map 中创建条目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 Map 而不是对象映射来声明一些键和值.但是 Typescript 似乎不支持 ES6 Map 的索引类型,是否正确,是否有任何解决方法?

I would like to use Map instead of object map to declare some keys and values. But Typescript doesn't seem to support index types for ES6 Map, is that correct and are there any workarounds?

此外,我还想让值类型安全,以便映射中的每个条目都具有与键对应的值的正确类型.

Additionally, I would like to make the values type-safe as well so that each entry in the map has the correct type for the value corresponding to the key.

这是一些描述我想要实现的目标的伪代码:

Here is some pseudo-code that describes what I am trying to achieve:

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],
]);

部分描述我的用例的更多代码

more code that partially describes my use case

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]
]);

推荐答案

这是我能想象得到的最接近的结果,虽然我仍然不明白为什么我们不直接使用普通对象:

Here's the closest I can imagine getting, although I still don't understand why we don't just use plain objects to begin with:

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] }>;
  new <T>(): ObjectMap<Partial<T>>;
  readonly prototype: ObjectMap<any>;
}

const ObjectMap = Map as ObjectMapConstructor;

想法是制作一个新的接口,ObjectMap,它专门依赖于一个对象类型O来确定它的键/值关系.然后你可以说 Map 构造函数可以充当 ObjectMap 构造函数.我还删除了任何可以更改实际存在的键的方法(并且 has() 方法也是多余的 true).

The idea is to make a new interface, ObjectMap, which is specifically dependent on an object type O to determine its key/value relationship. And then you can say that the Map constructor can act as an ObjectMap constructor. I also removed any methods that can change which keys are actually present (and the has() method is redundantly true also).

我可以费力地解释每个方法和属性定义,但这需要大量的类型转换.简而言之,您要使用 K extends keyof OO[K] 来表示通常由 KV<表示的类型/code> 在 Map.

I can go through the trouble of explaining each method and property definition, but it's a lot of type-juggling. In short you want to use K extends keyof O and O[K] to represent the types normally represented by K and V in Map<K, V>.

构造函数有点烦人,因为类型推断不能按您希望的方式工作,因此保证类型安全分为两个步骤:

The constructor is a bit more annoying in that type inference doesn't work the way you'd like, so guaranteeing type safety comes in two steps:

// 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;

如果您的 myMapInferredTypeObjectMap 不匹配(例如,您缺少键或具有错误的值类型),则 myMap 会给你错误.

If your myMapInferredType doesn't match ObjectMap<Values> (e.g., you are missing keys or have the wrong value types) then myMap will give you errors.

现在您可以使用 myMap 作为 ObjectMap,类似于使用 Map 实例的方式,使用 get()set(),它应该是类型安全的.

Now you can use myMap as an ObjectMap<Values>, similarly to how you'd use a Map instance, with get() and set(), and it should be type safe.

再次请注意......对于一个更复杂的对象来说,这似乎需要大量的工作,这些对象的类型更复杂,而且没有比普通对象更多的功能.我会认真警告任何使用 Map 其键是 keyof any(即 string | number | symbol)的子类型的人强烈考虑使用普通对象,并确保您的用例确实需要 地图.

Please note again... this seems like a lot of work for a more complex object with trickier typings and no more functionality than a plain object. I would seriously warn anyone using a Map whose keys are subtypes of keyof any (that is, string | number | symbol) to strongly consider using a plain object instead, and be sure that your use case really necessitates a Map.

代码操场链接

这篇关于打字稿:如何根据对象键/值类型在 ES6 Map 中创建条目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆