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

Seb*_*res 7 typescript

在 TypeScript 中你可以这样做

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

type objKeys = keyof typeof obj
Run Code Online (Sandbox Code Playgroud)

并且objKeys是联合型'123-123' | '456-456'

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

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

type mapKeys = ???
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 5

如果你new Map<string, number>(...)这样写,那么你就明确地给出了Map一个string键类型,这意味着它将允许你set()get()字符串值键。如果您想将键限制为字符串文字类型的特定联合,那么您将需要更改构造. 例如,如果您提前定义,则可以使用它来代替:MapMapKeysstring

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

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


既然您说用例是Map恒定的(因此不仅它的键受到限制,而且它的值也不会改变),那么TypeScript 提供的接口可能会更好ReadonlyMap<K, V>地为您服务。运行时没有ReadonlyMap,但 TypeScript 编译器将允许您将实例分配Map给 类型的变量ReadonlyMap

如果我们希望编译器推断MapKeys,我们可能希望将构造函数的键类型限制string为,这会给编译器一个字符串提示。

ReadonlyMap<K, V>这两者都意味着创建新的K限制的辅助函数string将有所帮助:

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>
Run Code Online (Sandbox Code Playgroud)

现在我们可以看到map它的键是强类型的。我们现在可以MapKeys从中定义。有多种方法可以做到这一点;一种是使用关键字条件类型推断infer

type MapKeys = typeof map extends ReadonlyMap<infer K, any> ? K : never;
// type MapKeys = "123-123" | "456-456"
Run Code Online (Sandbox Code Playgroud)

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

type MapKeys = Parameters<typeof map["get"]>[0]
// type MapKeys = "123-123" | "456-456"
Run Code Online (Sandbox Code Playgroud)

Playground 代码链接