简体   繁体   中英

TypeScript: Factory function for union types

I have a custom Factory type that lets me create factory functions.

export type Factory<T> = (state?: Partial<T>) => T;

I have an <Avatar /> React component that has a union type as prop because there can be two versions of it (just the image, or clickable image). In other words, the component can have either or props.

type AvatarOnlyProps = {
  alt: string;
  src: string;
};

type ClickableAvatarProps = {
  alt: string;
  label: string;
  onClick:
    | ((event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void)
    | undefined;
  src: string;
};

export type AvatarProps = AvatarOnlyProps | ClickableAvatarProps;

I want to create a factory function for the avatar's props. I tried typing it like this:

const createProps: Factory<AvatarProps> = ({
  alt = 'Your avatar',
  label,
  onClick,
  src: source = '/images/default-avatar.png',
} = {}) => ({ alt, label, onClick, src: source });

But TypeScript complains:

Property 'label' does not exist on type 'Partial<AvatarOnlyProps> | Partial<ClickableAvatarProps>'.

in the arguments of the factory function.

在此处输入图像描述

How can I make the error go away? How can I get TypeScript to under stand, that in this factory function either both onClick and label will be supplied, or neither?

You can't destructure a property from a union unless it is included on all members of the union. label and onClick don't have any definition on AvatarOnlyProps .

How can I get TypeScript to under stand, that in this factory function either both onClick and label will be supplied, or neither?

Based on how you've described your Factory , it isn't actually the case that you have both or neither. A Factory of T takes an incomplete Partial<T> and returns a complete T . Since it accepts Partial<T> it seems like it could accept a Partial<ClickableAvatarProps> which has label but not onClick , or vice-versa.

You are setting default values for alt and src , but not for label or onClick . So if only one is supplied then only one will be returned too.

In order to properly describe this behavior, we need more than just the union of two possibilities. createProps accepts an object with all four properties where all are optional. It returns an object with all four property keys where alt and src will always be string , but label and onClick might be undefined .

We need to define that returned object. Here's one way:

type AvatarProps = AvatarOnlyProps & Partial<ClickableAvatarProps>;

You can now define createProps as Factory<AvatarProps> and you won't have any issues.

I was able to solve it for my use case by creating the factor function with the minimal keys populated:

const createProps: Factory<AvatarProps> = ({
  alt = 'Your avatar',
  src: source = '/images/default-avatar.png',
  ...props
} = {}) => ({
  alt,
  src: source,
  ...props,
});

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