'Restricting object key type with const

I am able to define an object key type from a union like:

type Action = 'foo' | 'bar' | 'baz';

type UserStates = 'active' | 'deleted' | 'dishonoured';

type Permission = {
  [key in UserStates]: Action[];
};

However, I have a scenario where the options are const literals like:

const UserStates = ['active', 'deleted', 'dishonoured'] as const;
const Action = ['foo', 'bar', 'baz'] as const;

type Permission = {
  [key: typeof UserStates[number]]: typeof Action[number][];
};

This results in:

error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.

How should I define the key signature if this scenario?

Working Example for union types:

type Action = 'foo' | 'bar' | 'baz';

type UserStates = 'active' | 'deleted' | 'dishonoured';

type Permission = {
  [key in UserStates]: Action[];
};

export const PERMISSIONS: Permission = {
  active: ['foo', 'bar', 'baz'],
  deleted: ['bar'],
  dishonoured: ['baz'],
};

const hasPermission = (userState: UserStates, action: Action): boolean =>
  PERMISSIONS[userState].some((x) => x === action);

console.info(hasPermission('active', 'foo'));
console.info(hasPermission('deleted', 'foo'));


Solution 1:[1]

You almost had it:

type Permission = {
  [key in typeof UserStates[number]]: typeof Action[number][];
};

Solution 2:[2]

A Record instead will work:

type Permission = Record<typeof UserStates[number], typeof Action[number][]>;

This results in the type:

type Permission = {
    active: ("foo" | "bar" | "baz")[];
    deleted: ("foo" | "bar" | "baz")[];
    dishonoured: ("foo" | "bar" | "baz")[];
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tobias S.
Solution 2 CertainPerformance