Given the following setup:
enum FormGroups {
customer = 'customer',
address = 'address',
}
interface Customer {
'firstName': string;
}
interface Address {
'street': string;
}
interface SomeFormEvent {
valid: boolean;
forms: {
[FormGroups.customer]: {
values: Customer,
},
[FormGroups.address]: {
values: Address,
}
}
}
Now the following is what I would like to use:
type Forms = Customer | Address
However, this is static. If in the future a form is added in the SomeFormEvent like:
interface SomeFormEvent {
valid: boolean;
forms: {
[FormGroups.customer]: {
values: Customer,
},
[FormGroups.address]: {
values: Address,
},
// a form is added here:
[FormGroups.preferences]: {
values: Preferences,
},
}
}
I'd have to manually update the 'Forms' type:
// manually have to add 'Preferences'
type Forms = Customer | Address | Preferences
Is there a more dynamic way to create the Forms type?
I've tried the following:
enum FormGroups {
customer = 'customer',
address = 'address',
}
interface Customer {
'firstName': string;
}
interface Address {
'street': string;
}
interface SomeFormEvent {
valid: boolean;
forms: {
[FormGroups.customer]: {
values: Customer,
},
[FormGroups.address]: {
values: Address,
},
}
}
type Forms = { [K in keyof SomeFormEvent['forms']]: SomeFormEvent['forms'][K]['values'] }
function foo(): Forms {
return {
// error: Type '{ firstName: string; street: string; }' is
// not assignable to type 'Forms'. Object literal may only
// specify known properties, and 'firstName' does not exist
// in type 'Forms'.
firstName: 'sdfdsf',
street: 'sdffds'
}
}
But that does not work.
CodePudding user response:
Yes, we can create this union using indexed access types.
type Forms = SomeFormEvent["forms"][keyof SomeFormEvent["forms"]]["values"]
// ^? Customer | Address