如何在TypeScript中的嵌套对象中键入具有函数参数类型的函数参数?
我试图键入一个函数,其中参数类型应该从嵌套对象中的值推断出来。如何推断深度嵌套对象中的函数参数类型 例如:如何在TypeScript中的嵌套对象中键入具有函数参数类型的函数参数?,typescript,Typescript,我试图键入一个函数,其中参数类型应该从嵌套对象中的值推断出来。如何推断深度嵌套对象中的函数参数类型 例如: export enum Role { USER = 'user', ADMIN = 'admin', OWNER = 'owner', PRIMARY_OWNER = 'primaryOwner', } // Add as needed. Formatted as 'resource:action'? export type Ability = | 'users:cr
export enum Role {
USER = 'user',
ADMIN = 'admin',
OWNER = 'owner',
PRIMARY_OWNER = 'primaryOwner',
}
// Add as needed. Formatted as 'resource:action'?
export type Ability =
| 'users:create'
| 'users:edit'
| 'reports:view'
| 'settings:view';
type StaticAbilities = readonly Ability[];
type DynamicAbility = (data: any) => boolean;
type DynamicAbilities = { readonly [key in Ability]?: DynamicAbility };
export type Abilities = {
readonly [R in Role]?: {
readonly static?: StaticAbilities;
readonly dynamic?: DynamicAbilities;
}
};
/**
* A configuration object containing allowed abilities for specific roles.
*/
export const ABILITIES: Abilities = {
user: {
dynamic: {
// THIS IS AN EXAMPLE OF DYNAMIC RULES
'users:edit': ({
currentUserId,
userId,
}: {
/** Current users ID */
currentUserId: string;
/** User ID trying to be edited */
userId: string;
}) => {
if (!currentUserId || !userId) return false;
return currentUserId === userId;
},
},
},
admin: {
static: ['reports:view', 'settings:view'],
},
owner: {
static: ['reports:view', 'settings:view'],
},
primaryOwner: {
static: ['reports:view', 'settings:view'],
},
};
export const can = ({
role,
ability,
data,
}: {
role: Role;
ability: Ability;
data?: any;
}): boolean => {
const permissions = ABILITIES[role];
// Return false if role not present in rules.
if (!permissions) {
return false;
}
const staticPermissions = permissions.static;
// Return true if rule is in role's static permissions.
if (staticPermissions && staticPermissions.includes(ability)) {
return true;
}
const dynamicPermissions = permissions.dynamic;
if (dynamicPermissions) {
const permissionCondition = dynamicPermissions[ability];
// No rule was found in dynamic permissions.
if (!permissionCondition) {
return false;
}
return permissionCondition(data);
}
// Default to false.
return false;
};
给定一个特定的角色
和能力
,我想在can()
中键入数据
,作为能力
中定义的函数参数类型(如果存在)。如果它不存在,那么我不希望需要数据
我希望数据的类型
是{currentUserId:string;userId:string}
的必需类型,当角色为role.USER
且能力为'users:edit'
时,您可以执行一些条件类型魔术来提取适当的参数类型,前提是能力
是使用对象文本的实际类型键入的(不仅仅是能力
)。我们可以使用一个额外的函数来帮助编译器推断正确的类型
export enum Role {
USER = 'user',
ADMIN = 'admin',
OWNER = 'owner',
PRIMARY_OWNER = 'primaryOwner',
}
// Add as needed. Formatted as 'resource:action'?
export type Ability =
| 'users:create'
| 'users:edit'
| 'reports:view'
| 'settings:view';
type StaticAbilities = readonly Ability[];
type DynamicAbility = (data: any) => boolean;
type DynamicAbilities = { readonly [key in Ability]?: DynamicAbility };
export type Abilities = {
readonly [R in Role]?: {
readonly static?: StaticAbilities;
readonly dynamic?: DynamicAbilities;
}
};
function createAbilities<A extends Abilities>(a: A) {
return a;
}
export const ABILITIES = createAbilities({
user: {
dynamic: {
// THIS IS AN EXAMPLE OF DYNAMIC RULES
'users:edit': ({
currentUserId,
userId,
}: {
/** Current users ID */
currentUserId: string;
/** User ID trying to be edited */
userId: string;
}) => {
if (!currentUserId || !userId) return false;
return currentUserId === userId;
},
},
},
admin: {
static: ['reports:view', 'settings:view'],
},
owner: {
static: ['reports:view', 'settings:view'],
},
primaryOwner: {
static: ['reports:view', 'settings:view'],
},
});
type ExtractDynamicParameter<R extends Role, A extends Ability> = typeof ABILITIES[R] extends { dynamic: Record<A, (p: infer P) => boolean> } ? { data : P } : { data?: undefined}
export const can = <R extends Role, A extends Ability>({
role,
ability,
data,
}: {
role: R;
ability: A;
} & ExtractDynamicParameter<R, A>): boolean => {
const permissions = ABILITIES[role as Role] as Abilities[Role]; // Needed assertions
// Return false if role not present in rules.
if (!permissions) {
return false;
}
const staticPermissions = permissions.static;
// Return true if rule is in role's static permissions.
if (staticPermissions && staticPermissions.includes(ability)) {
return true;
}
const dynamicPermissions = permissions.dynamic;
if (dynamicPermissions) {
const permissionCondition = dynamicPermissions[ability];
// No rule was found in dynamic permissions.
if (!permissionCondition) {
return false;
}
return permissionCondition(data);
}
// Default to false.
return false;
};
can({ role: Role.USER, ability: "users:edit", data: { currentUserId: "", userId: "" } }) // ok
can({ role: Role.USER, ability: "users:edit", data: {} }) // err
can({ role: Role.USER, ability: "users:edit" }) // err
导出枚举角色{
用户='USER',
ADMIN='ADMIN',
所有者='所有者',
主要所有者='primaryOwner',
}
//根据需要添加。是否格式化为“资源:操作”?
出口型能力=
|“用户:创建”
|'用户:编辑'
|“报告:视图”
|'设置:查看';
类型StaticAbilities=只读能力[];
类型DynamicAbility=(数据:any)=>boolean;
类型DynamicAbility={readonly[输入能力]?:DynamicAbility};
导出类型能力={
只读[R在角色中]?:{
只读静态?:静态能力;
只读动态?:动态能力;
}
};
函数createAbilities(a:a){
返回a;
}
导出常量能力=创建能力({
用户:{
动态:{
//这是一个动态规则的示例
“用户:编辑”:({
当前用户ID,
用户ID,
}: {
/**当前用户ID*/
currentUserId:字符串;
/**试图编辑的用户ID*/
userId:string;
}) => {
如果(!currentUserId | |!userId)返回false;
return currentUserId==用户ID;
},
},
},
管理员:{
静态:[“报告:视图”,“设置:视图”],
},
所有者:{
静态:[“报告:视图”,“设置:视图”],
},
主要所有者:{
静态:[“报告:视图”,“设置:视图”],
},
});
type ExtractDynamicParameter=typeof能力[R]扩展了{dynamic:Record boolean>}?{data:P}:{data?:未定义}
导出常量can=({
角色
能力,,
数据,
}: {
角色:R;
能力:A;
}&ExtractDynamicParameter):布尔=>{
const permissions=ABILITIES[role as role]as ABILITIES[role];//需要的断言
//如果规则中不存在角色,则返回false。
如果(!权限){
返回false;
}
const staticPermissions=permissions.static;
//如果规则处于角色的静态权限中,则返回true。
if(staticPermissions&&staticPermissions.includes(能力)){
返回true;
}
const dynamicPermissions=permissions.dynamic;
if(动态许可证){
常量许可条件=动态许可[能力];
//在动态权限中找不到规则。
如果(!许可条件){
返回false;
}
返回许可条件(数据);
}
//默认为false。
返回false;
};
can({role:role.USER,能力:“users:edit”,数据:{currentUserId:,userId:}})//确定
can({role:role.USER,能力:“用户:编辑”,数据:{})//err
can({role:role.USER,ability:“users:edit”})//错误
我想让可以({role:role.USER,ability:'users:edit'})需要时,代码>因数据丢失而失败。“我想这样做的方法是做某种函数重载?”埃里克·泰勒改变了答案,使之与上面的请求一样工作。