Skip to content

Instantly share code, notes, and snippets.

@dmmulroy
Last active November 11, 2023 23:05
Show Gist options
  • Save dmmulroy/0cbbbde210dbfe5e193a32130a22a32a to your computer and use it in GitHub Desktop.
Save dmmulroy/0cbbbde210dbfe5e193a32130a22a32a to your computer and use it in GitHub Desktop.
Incorrect TS inference
type RoleName = "admin" | "member";
type AdminPermission = "add-member" | "ban-member" | "delete-any-post";
type MemberPermission = "add-post" | "read-post" | "edit-post" | "delete-post";
type Permission = AdminPermission | MemberPermission;
type RoleToPermissions = {
admin: readonly AdminPermission[];
member: readonly MemberPermission[];
};
type Role<T extends RoleName> = Readonly<{
role: T;
permissions: RoleToPermissions[T];
}>;
type RolePermission = Role<"admin"> | Role<"member">;
// Internal RBAC library helper function
function getPermissionsForMember(): readonly MemberPermission[] {
return ["add-post", "read-post", "edit-post", "delete-post"];
}
// Internal RBAC library helper function
function getPermissionsForAdmin(): readonly AdminPermission[] {
return ["add-member", "ban-member", "delete-any-post"];
}
// Exposed RBAC library function
function getPermisisonsForRole<T extends RolePermission>(
role: T["role"]
): T["permissions"] {
return (
{
admin: getPermissionsForAdmin(),
member: getPermissionsForAdmin(), // woops we accidentally used the wrong function, but get no error
} as const
)[role];
}
const memberPermissions = getPermisisonsForRole<Role<"member">>("member");
// ^? const memberPermissions: readonly ["add-post", "read-post", "edit-post", "delete-post"]
// BOOM 💥 - our member now has admin permissions
console.log(memberPermissions); // ["add-member", "ban-member", "delete-any-post"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment