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.incorrectPassword
is 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.