I seem to be missing something, but the | AlertDynamic seems to break my typing suggestion
type Alert = {
type: string
variant: 'danger' | 'warning' | 'success' | 'info'
message: string
}
type AlertDynamic = (arg: string) => Alert
export const alertTypeAuthentication: Record<string, Alert | AlertDynamic> = {
incorrectPassword: {
type: 'Incorrect password',
variant: 'danger',
message: 'Adgangskode er forkert.',
},
passwordCreated: (email: string) => ({
type: 'Password created',
variant: 'success',
message: `We sendt a message to ${email}`,
}),
} as const
When just using Record<string, Alert > I don't see a type error for alertTypeAuthentication.incorrectPassword.message
CodePudding user response:
Typescript won't know that incorrectPassword is of type Alert, it will know it's either Alert or AlertDynamic. It doesn't matter that you used as const since the Record type is on the variable alertTypeAuthentication.
You can solve this by letting typescript infer the type itself, like this:
export const alertTypeAuthentication = {
incorrectPassword: {
type: 'Incorrect password',
variant: 'danger',
message: 'Adgangskode er forkert.',
},
passwordCreated: (email: string) => ({
type: 'Password created',
variant: 'success',
message: `We sendt a message to ${email}`,
}),
}
Or create a wrapper type like this:
type Alert = {
type: string
variant: 'danger' | 'warning' | 'success' | 'info'
message: string
}
type AlertDynamic = (arg: string) => Alert
type AlertTypes = {
incorrectPassword: Alert
passwordCreated: AlertDynamic
}
export const alertTypeAuthentication: AlertTypes = {
incorrectPassword: {
type: 'Incorrect password',
variant: 'danger',
message: 'Adgangskode er forkert.',
},
passwordCreated: (email: string) => ({
type: 'Password created',
variant: 'success',
message: `We sendt a message to ${email}`,
}),
}
CodePudding user response:
alertTypeAuthentication is a Record<string, Alert | AlertDynamic>, which means that the compiler doesn't know that alertTypeAuthentication.incorrectPasswordis an Alert or alertTypeAuthentication.passwordCreated is an AlertDynamic, only that they can be either an Alert or AlertDynamic.
Type predicates allow you to write custom type guarding logic. Since Alert is an object type and AlertDynamic is a function type, we can narrow between the types using those properties on some argument of Alert | AlertDynamic:
type Alert = {
type: string
variant: 'danger' | 'warning' | 'success' | 'info'
message: string
}
type AlertDynamic = (arg: string) => Alert
function isAlert(a: Alert | AlertDynamic): a is Alert {
return typeof a === 'object';
}
function isAlertDynamic(a: Alert | AlertDynamic): a is AlertDynamic {
return typeof a === 'function';
}
const alertTypeAuthentication: Record<string, Alert | AlertDynamic> = {
incorrectPassword: {
type: 'Incorrect password',
variant: 'danger',
message: 'Adgangskode er forkert.',
},
passwordCreated: (email: string) => ({
type: 'Password created',
variant: 'success',
message: `We sendt a message to ${email}`,
}),
};
if (isAlert(alertTypeAuthentication.incorrectPassword)) {
console.log(alertTypeAuthentication.incorrectPassword.type);
}
if (isAlertDynamic(alertTypeAuthentication.passwordCreated)) {
console.log(alertTypeAuthentication.passwordCreated("foo"));
}
Check it out on the playground.
