1

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?

2
  • In my opinion you're better off changing your data structure so that your the differences between array items are contained within an object. e.g. {id: 1, component: "mockup", data: { ... } }. I know this doesn't address your problem but you'll find it much easier to type things this way rather than having an array of different types. Commented Jun 27, 2022 at 9:03
  • @Alex This data structure actually comes from Strapi's API in regards to their dynamic zones so I am not really in a place to change it. Commented Jun 27, 2022 at 9:10

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.