es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Utilizando discriminating unions en las props de React con un valor por defecto.

Tengo un componente de React que tiene 3 variantes. Mis props están tipadas de la siguiente manera:

enum VariantType {
  VARIANT_1 = "variant1",
  VARIANT_2 = "variant2",
  VARIANT_3 = "variant3",
}

type BaseProps = {
  a: string;
}

type Variant1Props = BaseProps & {
  variant: VariantType.VARIANT_1;
  b: never;
}

type Variant2Props = BaseProps & {
  variant: VariantType.VARIANT_2;
  b: number;
}

type Variant3Props = BaseProps & {
  variant: VariantType.VARIANT_3;
  b: boolean;
}

export const FancyComponent: FunctionComponent<variant1props |="" variant2props="" |="" variant3props=""> = (props) => {
  const someNumber = props.variant === VariantType.VARIANT_2 ? props.b : 123; 
   ...
}

En la asignación de someNumber, TS sabe que b está definida porque verifiqué la variante. Cuando utilizo el componente, TS se queja cuando b está presente cuando es VARIANT_1 y se queja cuando b no está presente/no es un número cuando es VARIANT_2 (lo mismo para VARIANT_3).

Lo que estoy buscando ahora es una forma de hacer que la opción variant sea opcional cuando se utiliza el componente y que esta opción tenga un valor predeterminado de VARIANT_1. Además, me gustaría poder establecerla en VARIANT_2 o VARIANT_3 sin perder la seguridad de tipos que tengo actualmente.

Intenté algunas cosas con genéricos y demás, pero no pude hacer que funcionara.

Estoy utilizando Typescript 4.5.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Para hacer opcional la opción variant y establecerla por defecto en VARIANT_1, puedes modificar el tipo BaseProps para incluir una propiedad variant opcional que tenga como valor por defecto VariantType.VARIANT_1. Luego, puedes actualizar los tipos Variant1Props, Variant2Props y Variant3Props para que extiendan BaseProps en lugar de BaseProps & { variant: VariantType }.

    type BaseProps = {
      a: string;
      variant?: VariantType.VARIANT_1; // hacer variant opcional y establecerlo como VARIANT_1 por defecto
    }
    
    type Variant1Props = BaseProps & {
      variant?: VariantType.VARIANT_1; // hacer variant opcional
      b: never;
    }
    
    type Variant2Props = BaseProps & {
      variant?: VariantType.VARIANT_2; // hacer variant opcional
      b: number;
    }
    
    type Variant3Props = BaseProps & {
      variant?: VariantType.VARIANT_3; // hacer variant opcional
      b: boolean;
    }
    

    Luego, en la definición de FancyComponent, puedes utilizar una unión discriminada con un caso por defecto para asegurar que TS conozca el tipo de props.variant y garantice el tipo correcto para props.b.

    export const FancyComponent: FunctionComponent<variant1props |="" variant2props="" |="" variant3props=""> = (props) => {
      switch (props.variant || VariantType.VARIANT_1) { // establecer VARIANT_1 como valor por defecto si props.variant es undefined
        case VariantType.VARIANT_1:
          return (
            <div>
              Variante 1: {props.a}
            </div>
          );
        case VariantType.VARIANT_2:
          return (
            <div>
              Variante 2: {props.a}, {props.b.toFixed(2)} // TS sabe que este es un tipo número
            </div>
          );
        case VariantType.VARIANT_3:
          return (
            <div>
              Variante 3: {props.a}, {props.b ? 'sí' : 'no'} // TS sabe que este es un tipo booleano
            </div>
          );
        default:
          throw new Error(`Variante no válida: ${props.variant}`);
      }
    }
    

    De esta manera, puedes utilizar FancyComponent tanto especificando como no especificando la propiedad variant, y seguir manteniendo la seguridad de tipos. Por ejemplo:

    <fancycomponent a="hola"></fancycomponent> // se establece VARIANT_1 por defecto
    
    <fancycomponent a="hola" variant="{VariantType.VARIANT_2}" b="{42}"></fancycomponent>
    
    <fancycomponent a="hola" variant="{VariantType.VARIANT_3}" b="{true}"></fancycomponent> 
    

    Si intentas pasar un valor variant que no forma parte del enum VariantType, TS lanzará un error, asegurando que tu código siga siendo seguro en cuanto a tipos.

Comments are closed.