8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Typescript:如何根据对象键/值类型在 ES6 Map 中进行输入

MSeifert 2月前

54 0

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

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

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

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

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、本帖标题:Typescript:如何根据对象键/值类型在 ES6 Map 中进行输入
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由MSeifert在本站《typescript》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 编辑:在原始帖子中添加了更多代码。我更喜欢上述结构而不是对象,因为我正在考虑使用符号作为键,而且它看起来更有条理/更严格。

  • 正确,Juan,我的第一个用例实际上已经解决了。我也考虑过将这些键、值声明为数组,并进行严格的类型检查,然后对 Map 进行不太严格的类型检查,但我还不确定这是否会起作用。

  • 这是我能想象到的最接近的,虽然我仍然不明白为什么我们不从一开始就使用普通对象:

    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 也是多余的)。

    我可以不厌其烦地解释每个方法和属性定义,但这需要很多类型的处理。简而言之,你想使用 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 .

    游乐场链接到代码

  • 感谢您抽出时间来详细解释这一点。它目前可以正常工作,但我必须考虑是否值得在普通对象上使用它,特别是因为我可能必须将 key 转换为 key。

  • 你关心插入顺序吗?ObjectMap不知道 O 中键的顺序,所以在编译时它不会帮你解决这个问题。不确定你为什么关心顺序,但我可以想象一种类型确实保证了元组类型而不是数组的顺序……但这更加复杂,我想我现在需要尖叫着逃跑。‍

  • 哈哈哈,好吧,现在我想起来,我可能不必要地吓到了你,插入顺序并不重要,因为它是一个地图,所以每个键只能表示一次。

  • @jcalz 从那时起 TypeScript 有什么变化吗?我也很想使用 Map,因为我需要能够对属性进行排序。

返回
作者最近主题: