简体   繁体   中英

Typescript type assertion for "dynamic" record

I seem to be missing something, but the | AlertDynamic | 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

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}`,
  }),
}

alertTypeAuthentication is a Record<string, Alert | AlertDynamic> 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 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM