简体   繁体   中英

How can I use typescript to destructure from a complex type?

Consider this use case: I have a React functional component that I'd like to implement with the props being destructured (for consistency with all the other components).

This is a checkbox which has two possible roles. If it is associated with the full amount of data, it can be interacted with, and if it is associated with limited data, it will display a name, but the elements cannot be interacted with.

This can be simply implemented by the use of two independent components, and I may just go with that approach, but I wonder about the extent to which it is possible to extend and flesh out an existing component in code in this way.

So, i'm starting with a checkbox component that takes this type for props:

export interface CheckboxProps {
  checked: boolean;
  id: string;
  disabled?: boolean;
  disabledTooltipLabel?: string;
  onChange: VoidFunction;
  labelOrientation?: CheckboxLabelOrientation;
  label?: string;
  className?: string;
}

With this destructuring for the props to start off the function component definition:

export const Checkbox: React.FunctionComponent<CheckboxProps> = ({
  checked,
  disabled,
  disabledTooltipLabel,
  id,
  onChange,
  labelOrientation = "left",
  label = "",
  className,
}) => {
...

and I'm hoping to extend this with minimal refactoring to allow me to feed in a much simplified input consisting solely of label, eg with a tweaked type. However now if I try to actually use this, the destructuring is impossible:

export interface CheckboxProps {                      
  checked: boolean;                                   
  id: string;                                         
  disabled?: boolean;                                 
  disabledTooltipLabel?: string;                      
  onChange: VoidFunction;                             
  labelOrientation?: CheckboxLabelOrientation;        
  label?: string;                                     
  className?: string;                                 
}                                                     
type CheckboxBarebones = Pick<CheckboxProps, "label">;
                                                      
export const Checkbox: React.FunctionComponent<       
  CheckboxBarebones | CheckboxProps                   
> = (props) => {           
  const { label } = props;                           
  if ("checked" in props) {                           
    var {                                             
      checked,                                        
      disabled,                                       
      disabledTooltipLabel,                           
      id,                                             
      onChange,                                       
      labelOrientation = "left",                      
      className,                                      
    } = props;                                        
  }
...                                                   

This might work in theory, but I get vars-on-top and a torrent of block-scoped-var eslint errors. Is there any kind of "lite" destructuring I could do? Even if I manually pull values out of the props, how could I even go about doing it cleanly if I want to use const ?

Since this is so completely horrific, I kept working and came up with:

export const Checkbox: React.FunctionComponent<   
  Pick<CheckboxProps, "label"> | CheckboxProps    
> = (props) => {                                  
  const {                                         
    label,                                        
    checked,                                      
    disabled,                                     
    disabledTooltipLabel,                         
    id,                                           
    onChange,                                     
    labelOrientation = "left",                    
    className,                                    
  } = props as Partial<CheckboxProps>;           
...

This seems like an improvement, but it still has a type cast in it and cannot be directly destructured in arrow function parameter. Is there a way to avoid it? I like how this preserves a useful type contract for the function component's props, which is the important thing here.

You can keep the desired props type for consumers, but in the implementation define it as partial without casting:

const Checkbox: React.FC<Pick<CheckboxProps, "label"> | CheckboxProps> = ({
  label,
  checked,
  disabled,
  disabledTooltipLabel,
  id,
  onChange,
  labelOrientation = "left",
  className,
}: Partial<CheckboxProps>) => {
    return <>...</>;
}

When you have a union type like CheckboxBarebones | CheckboxPropsCheckboxBarebones | CheckboxProps you generally want to narrow the type before you destructure. This is because when you desctructure you are making each property into its own independent variable. So checking for the presence of checked won't refine any of the other property types. If you know the specific type before destructuring then the properties will get the correct types based on the object type.

You should also be aware that since label is an optional property, Pick<CheckboxProps, "label"> could be satisfied with an empty object. I'm assuming that you meant to make it required for CheckboxBarebones .

If we define a type union that requires it fulfill one of the two types, but also includes all properties as optional, you will be able to destructure. Be warned that you won't be able to simply switch between the two types, and will have to assume that all these variables might be undefined .

type EitherCheckboxProps = (CheckboxProps | {label: string}) & Partial<CheckboxProps>

export const Checkbox: React.FunctionComponent<EitherCheckboxProps> = ({
  checked,
  disabled,
  disabledTooltipLabel,
  id,
  onChange,
  labelOrientation = "left",
  label = "",
  className,
}) => {

Playground Link

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