是否可以将 Map 键提取为 TypeScript 中的类型?

Is it possible to extract Map keys as type in TypeScript?

在 TypeScript 中你可以做到

const obj = {
  '123-123': 1,
  '456-456': 2,
}

type objKeys = keyof typeof obj

objKeys是联合类型'123-123' | '456-456'

如果 obj 不是一个对象,而是一个地图,是否有类似的可能?

const map = new Map<string, number>([
  ['123-123', 1],
  ['456-456', 2],
])

type mapKeys = ???

如果你写 new Map<string, number>(...) 那么你明确地给 Map 一个 string 键类型,这意味着它将允许你 set()get() 和 string-valued 键。如果您想将键限制为特定的 union of string literal types,那么您将需要更改构造 Map 的方式。比如你提前定义了MapKeys,就可以用它来代替string:

type MapKeys = '123-123' | '456-456';
const map = new Map<MapKeys, number>([
  ['123-123', 1],
  ['456-456', 2],
]);

但这有点多余,而且与您的要求相反。您希望编译器根据传递给 Map 构造函数的参数 推断 MapKeys


既然你说用例是 Map 是常量(因此不仅它的键受到约束,而且它的值也不会改变),那么 [= 可能会更好地为你服务40=] 由 TypeScript 提供。在运行时没有 ReadonlyMap,但 TypeScript 编译器将允许您将 Map 实例分配给 ReadonlyMap.

类型的变量

并且如果我们希望编译器推断MapKeys,我们可能希望constrain构造函数的键类型为string,这给编译器一个提示strings.

这两个都意味着在 K 被限制为 string 的情况下创建新 ReadonlyMap<K, V> 的辅助函数将有所帮助:

function ReadonlyMapWithStringKeys<K extends string, V>(
  iterable: Iterable<[K, V]>): ReadonlyMap<K, V> {
  return new Map(iterable)
}

const map = ReadonlyMapWithStringKeys([
  ['123-123', 1],
  ['456-456', 2],
])
// const map: ReadonlyMap<"123-123" | "456-456", number>

现在我们可以看到map是一个键是强类型的类型。我们现在可以从中定义 MapKeys 。有多种方法可以做到这一点;一个正在使用 conditional type inference with the infer keyword

type MapKeys = typeof map extends ReadonlyMap<infer K, any> ? K : never;
// type MapKeys = "123-123" | "456-456"

或者您可以使用 the Parameters<T> type 等实用程序类型来询问编译器 map.get() 的第一个参数是什么:

type MapKeys = Parameters<typeof map["get"]>[0]
// type MapKeys = "123-123" | "456-456"

Playground link to code