TypeScript如何定义无泛型可扩展的带类型值Open Map类型?
解决方案
可以实现,不需要给State加泛型参数,核心是利用TypeScript 4.1+支持的带类型映射的索引签名,结合条件类型推断LookupKey上绑定的值类型。
完整实现代码
// 定义带泛型绑定的LookupKey,__valueType为仅类型层面生效的幽灵属性,不会影响运行时 export type LookupKey<T> = symbol & { __valueType: T }; // 无泛型参数的具体State类型 export type State = { [K in LookupKey<any>]: K extends LookupKey<infer T> ? T | undefined : never; };
效果验证
完全符合你要求的类型安全规则,且支持任意值类型的扩展,不需要修改State定义:
// 客户端可自定义任意值类型的LookupKey export const stringSymbol = Symbol('string') as LookupKey<string>; export const numberSymbol = Symbol('number') as LookupKey<number>; export const boolSymbol = Symbol('bool') as LookupKey<boolean>; export const userSymbol = Symbol('user') as LookupKey<{id: number, name: string}>; export function whatever(state: State) { // ✅ 类型匹配的操作正常通过 const stringValue: string | undefined = state[stringSymbol]; state[stringSymbol] = "hello"; const userValue: {id: number, name: string} | undefined = state[userSymbol]; state[userSymbol] = {id: 1, name: "test"}; // ❌ 类型不匹配的操作会抛出类型错误 const notAString: number | undefined = state[stringSymbol]; state[stringSymbol] = 456; state[boolSymbol] = "not boolean"; }
实现原理
- LookupKey的泛型参数通过幽灵类型绑定到symbol上,仅做类型标记,不会产生运行时代码
- State的索引签名会自动匹配所有LookupKey类型,通过
infer T动态提取每个LookupKey绑定的值类型作为对应属性的类型 - State本身是独立的具体类型,不需要携带任何泛型参数,后续新增任意类型的LookupKey都不需要调整State定义,天然支持扩展。
内容的提问来源于stack exchange,提问作者Ivan Dubrov
相关产品推荐
相关产品推荐

