Home > Enterprise >  Correctly type dynamic array items according to array element's attribute value
Correctly type dynamic array items according to array element's attribute value

Time:06-27

Say I have an array like so:

[
    { id: 1, component: "mockup", src: "some-link" },
    { id: 2, component: "header", title: "Some Title", subtitle: "Lorem ipsum..." }
]

I am supposed to render according to the component key's value. How can I tell typescript to expect additional attributes if the component attribute equals to this or not expect this attribute if the component value equals to that? Is this beyond the scope of Typescript?

CodePudding user response:

You can use a discriminated union:

interface Base {
  id: number;
  component: string;
}

interface MockupComponent extends Base {
  component: 'mockup';
  src: string;
}

interface HeaderComponent extends Base {
  component: 'header',
  title: string;
  subtitle: string;
}

type Component = MockupComponent | HeaderComponent;

const components: Component[] = [
    { id: 1, component: "mockup", src: "some-link" },
    { id: 2, component: "header", title: "Some Title", subtitle: "Lorem ipsum..." },
    { id: 3, component: "other"}, // error
    { id: 3, component: "header", src: "some-link"}, // error
];

Playground link

CodePudding user response:

You can also create a map containing every component type's required attributes (as if you were creating an interface) like so :

const componentNameToAttributesMap = new Map<string, string[]([
    ["mockup", ['id', 'component', 'src']],
    ["header", ['id', 'component', 'title', 'subtitle']] 
]);

During rendering, you can then look up for the current component's name to fetch its attributes :

renderComponent(componentObj) {
   if (!componentNameToAttributesMap.has(componentObj.component)) {
       throw new Error(`Unknown component ${componentObj.component}`)
   }
   
   // then we check if component has every required attribute :

   componentNameToAttributesMap.get(componentObj.component)
   .forEach(attr => {
       if (!componentObj.hasOwnProperty(attr)) {
           throw new Error(`Component ${componentObj.component} is supposed to have attribute ${attr}`)
       }
   }
   // then you can put your render logic here
}
   

Cheers

  • Related