reactjs - Randare dinamică cu React și Typescript


Folosind React cu TypeScript, încerc să redau dinamic componente în funcție de numele unui câmp

// Referencing all components
const components = {
  ComponentA,
  ComponentB,
};

// Dynamic render the component based on fieldName
const displayComponent = (
  fieldName: keyof typeof components,
  content: ComponentType
): React.ReactNode => {
  const FieldNameComponent = components[fieldName];
  return <FieldNameComponent item={content} />;
};

Tipurile mele:

type ComponentTypeA = {
  title: string;
  content: string;
};

type ComponentTypeB = {
  title: string;
  excerpt: string;
};

type ComponentType = ComponentTypeA | ComponentTypeB;

Compilatorul nu este mulțumit pentru că nu știe ce tip să folosească (Component1 sau Component2 care au proprietăți diferite). Primesc această eroare

Tipul 'ComponentType' nu poate fi atribuit tipului 'ComponentTypeA & ComponentTypeB'.

Tipul 'ComponentTypeA' nu poate fi atribuit tipului 'ComponentTypeB & ComponentTypeA'.

Tipul 'ComponentTypeA' nu are următoarele proprietăți din tipul 'ComponentTypeB': excerpt

Așadar, am încercat această metodă:

const displayComponent = (
  fieldName: keyof typeof components,
  content: ComponentType
) => {
  if (content instanceof ComponentTypeA) {
    return <ComponentA item={content} />;
  } else if (content instanceof ComponentTypeB) {
    return <ComponentB item={content} />;
  }
};

Dar eroarea rămâne chiar și cu testul instanceof.

Răspuns #1

Ai putea folosi un gard de tip:

function isComponentTypeA(type: unknown): type is ComponentTypeA {
    return type && "title" in type && "content" in type;
}

Și ai folosi-o în condiția:

  if (isComponentTypeA(content)) {
    return <ComponentA item={content} />; // inferred as ComponentTypeA
  } else if (isComponentTypeB(content)) { // another type guard if needed
    return <ComponentB item={content} />; // inferred as ComponentTypeB
  }

Răspuns #2

Am ajuns să rezolv această eroare în felul următor:

const displayComponent = (
  content: ComponentType
): React.ReactNode => {
  switch (content.fieldName) {
    case "ComponentA":
      return <ComponentA item={ content as ComponentAType}/>
    case "ComponentB":
      return <ComponentB item={ content as ComponentBType}/>
    default:
      break;
  }
};

Dar sunt destul de nefericit cu asta, pentru că dacă trebuie să mă ocup de multe componente, declarația switch va fi atât de lungă... O abordare mai bună este binevenită.