打字稿:映射的键与值的类型相同,而不限制键的类型

Lio*_*Tay 2 generics dictionary typescript

有没有办法进行以下类型检查?

const map = new Map()

map.set('stringkey', 'stringvalue') // OK
map.set(5, 10) // OK
const objectKey = { name: 'bob' }
map.set(objectKey, { name: 'alice' }) // OK

const stringValue: string = map.get('stringkey') // OK, but is typed as 'any'
const objectValue: { name: string } = map.get(objectKey) // OK, but is typed as 'any'

map.set('string', 5) // Error
map.set({ name: 'bob' }, { food: 'cake' }) // Error
Run Code Online (Sandbox Code Playgroud)

使用默认值

const map = new Map<any, any>()
Run Code Online (Sandbox Code Playgroud)

有效,但在访问值时没有提供有用的类型,因为它们都被键入为 any。

jca*_*alz 5

这很有趣。我认为您继续的最佳方法是声明您自己的接口,它代表您的特定变体Map

interface SameKeyValueMap {
  clear(): void;
  delete(key: any): boolean;
  forEach(
    callbackfn: <T>(value: T, key: T, map: SameKeyValueMap) => void, thisArg?: any
  ): void;
  get<T>(key: T): T | undefined;
  has(key: any): boolean;
  set<T>(key: T, value: T): this;
  readonly size: number;
}

const map: SameKeyValueMap = new Map();
Run Code Online (Sandbox Code Playgroud)

主要以您想要的方式工作:

map.forEach((x, y) => (console.log(x === y))); // OK
map.set('stringkey', 'stringvalue') // OK
map.set(5, 10) // OK
const objectKey = { name: 'bob' }
map.set(objectKey, { name: 'alice' }) // OK
Run Code Online (Sandbox Code Playgroud)

以下必须更改,因为get()can return undefined

const stringValue: string | undefined = map.get('stringkey')
const objectValue: { name: string } | undefined = map.get(objectKey)
Run Code Online (Sandbox Code Playgroud)

这是你想要的错误:

map.set('string', 5) // Error
Run Code Online (Sandbox Code Playgroud)

但以下不是错误:

map.set({ name: 'bob' }, { food: 'cake' }) // accepted?
Run Code Online (Sandbox Code Playgroud)

如果你看一下,那是因为类型T被推断为{name: string, food?: undefined} | {name?: undefined, food: string}. 这与您传入的值一致的。类型推断在这种情况下有点像黑色艺术。

尝试处理的一个方法是改变set()签名,以降低优先级的推断位点之一T。这种方式T只能从其中一个参数中推断出来,而不是两者都推断出来。像这样:

interface SameKeyValueMap {
  clear(): void;
  delete(key: any): boolean;
  forEach(
    callbackfn: <T>(value: T, key: T, map: SameKeyValueMap) => void, thisArg?: any
  ): void;
  get<T>(key: T): T | undefined;
  has(key: any): boolean;
  set<T>(key: T, value: T & {}): this; // lowered priority of value
  readonly size: number;
}
Run Code Online (Sandbox Code Playgroud)

这似乎有效:

map.set({ name: 'bob' }, { food: 'cake' }) // error
Run Code Online (Sandbox Code Playgroud)

在这种情况下,T被推断为{name: string}并且{food: 'cake'}不可分配给它。所以这是一个错误。


希望有帮助。祝你好运!