簡體   English   中英

Typescript:如何根據 object 鍵/值類型在 ES6 Map 中輸入條目

[英]Typescript: How can I make entries in an ES6 Map based on an object key/value type

我想使用 Map 而不是 object map 來聲明一些鍵和值。 但是 Typescript 似乎不支持 ES6 Map 的索引類型,這是正確的,有什么解決方法嗎?

此外,我還想讓值類型安全,以便 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]
]);

這是我能想象得到的最接近的,雖然我仍然不明白為什么我們不只使用普通對象開始:

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 OO[K]來表示通常由Map<K, V>中的KV表示的類型。

構造函數有點煩人,因為類型推斷不能按照您想要的方式工作,因此保證類型安全分為兩個步驟:

// 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<Values>不匹配(例如,您缺少鍵或具有錯誤的值類型),那么myMap會給您錯誤。

現在您可以將myMap用作ObjectMap<Values> ,類似於您使用Map實例的方式,使用get()set() ,並且它應該是類型安全的。

請再次注意......對於一個更復雜的對象來說,這似乎需要做很多工作,它的類型更復雜,並且沒有比普通對象更多的功能。 我會嚴重警告任何使用其鍵是keyof any子類型(即string | number | symbol )的Map的人強烈考慮使用普通對象,並確保您的用例確實需要Map

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM