I have a constant object like so:
const shortcuts = {
save: {
label: 'ctrl+s',
action: (documentId: string) => saveDocument(documentId)
},
open: {
label: 'ctrl+o',
action: (name: string, type: string) => loadDocument(documentName, extension),
thisShouldError: 'thisShouldError' // not allowed property
},
// etc...
} as const
I would like to ensure all the shortcuts follow a desired shape shown below with no excess properties added.
interface DesiredShape {
label: string,
action: (...args: any[]) => void
}
In particular, I'd like the above definition of shortcuts
to error because open.thisShouldError
is not allowed. At the same time, I don't want to lose any type information, i.e. when I do shortcuts.save.action
, I want to get the actual type signature of the function as (documentId: string) => saveDocument(documentId)
, not just the generic (...args: any[]) => void
.
Is it possible to achieve this?
What I have tried so far:
I can use object literals limitations to do this, but then I lose the type information.
I also tried making sure the difference of the keys is never
as below, but it seems TS only takes the keys which are present everywhere into ActualKeys
.
type ActualKeys = keyof typeof shortcuts['save' | 'open']
type PermittedKeys = keyof DesiredShape
// does not work because ActualKeys does not include `thisShouldError`
assertNever(null as unknown as Exclude<ActualKeys,PermittedKeys>)