如何定义可逐层收窄的嵌套Record类型以构建权限对象?
如何为权限集合定义精确的嵌套对象类型
背景与现有代码
我有一组权限字符串集合,以及对应的类型工具:
const stringVals = [ 'create:user', 'update:user', 'delete:user', 'create:document', 'update:document', 'delete:document', 'delete:workflow', 'run:workflow', ] as const type StringVals = typeof stringVals[number] type PermissionVerb<T extends string> = T extends `${infer Action}:${string}` ? Action : never type PermissionKey<T extends string, V extends PermissionVerb<T>> = T extends `${V}:${infer Key}` ? Key : never
这些类型工具可以正确提取权限动作(Verb)和对应的资源键(Key):
type Verbs = PermissionVerb<StringVals>→'create' | 'update' | 'delete' | 'run'type CreateKeys = PermissionKey<StringVals, 'create'>→'user' | 'document'type RunKeys = PermissionKey<StringVals, 'run'>→'workflow'
预期目标
我需要定义一个嵌套对象类型,结构如下:
{ create: { user: true, document: true }, update: { user: true, document: true }, delete: { user: true, document: true, workflow: true, }, run: { workflow: true, } }
当前问题
我最初定义的类型无法精确约束内层对象的键:
type PermissionsObject<T extends string> = Record<PermissionVerb<T>, Record<PermissionKey<T, PermissionVerb<T>>, boolean>> function createPermissionsObject<T extends string>(permissions: Readonly<T[]>): PermissionsObject<T> { throw 'not implemented' }
这里的问题是,外层Record的每个动词对应的内层对象会包含所有可能的权限键,而非仅该动词对应的键。比如permissions.run.user不会触发类型错误,不符合预期。
解决方案
使用映射类型替代Record,为每个动词单独生成对应的资源键集合:
// 精确的嵌套权限对象类型 type PermissionsObject<T extends string> = { [Verb in PermissionVerb<T>]: Record<PermissionKey<T, Verb>, boolean> }
这个类型会遍历每个权限动词,为其生成仅包含对应资源键的内层对象。比如run对应的内层对象只会包含workflow键,访问不存在的键会立即触发类型错误。
配套的实现函数
可以实现一个类型安全的函数来生成该对象:
function createPermissionsObject<T extends string>(permissions: Readonly<T[]>): PermissionsObject<T> { const result: Partial<PermissionsObject<T>> = {} permissions.forEach(perm => { const [verb, key] = perm.split(':') as [PermissionVerb<T>, PermissionKey<T, PermissionVerb<T>>] if (!result[verb]) { result[verb] = {} as Record<PermissionKey<T, typeof verb>, boolean> } result[verb][key] = true }) return result as PermissionsObject<T> } // 使用示例 const permissions = createPermissionsObject(stringVals) permissions.run.workflow // ✅ 类型正确 permissions.run.user // ❌ 类型错误,符合预期
内容的提问来源于stack exchange,提问作者stackoverfloweth
相关产品推荐
相关产品推荐

