Skip to content
tezvyn:

Design a type-safe polymorphic component that renders as different HTML elements

Source: blog.logrocket.comHardHow cards are made

Design a type-safe polymorphic component that renders as different HTML elements

Tests TypeScript generics and prop forwarding in design systems. Answer: generic as constrained to keyof JSX.IntrinsicElements, merged with ComponentPropsWithoutRef<C>, omit clashes, forward ref with ElementRef.

What's really being asked

This question probes whether you can bridge React runtime behavior with compile-time TypeScript safety at the design-system layer. It specifically checks your fluency with generics, constraint syntax, mapped types like JSX.IntrinsicElements, and ref forwarding across unknown element types. Interviewers want to see that you understand the difference between a component that merely accepts an as prop and one that actually validates attributes against the chosen element.

The full answer

First, declare a generic as prop constrained to React.ElementType or keyof JSX.IntrinsicElements so only valid HTML tags or components are accepted. Second, compute the element-specific props by using React.ComponentPropsWithoutRef<C> and intersecting them with your own props, while strictly omitting any keys that clash with your custom API such as color or size. Third, handle ref forwarding correctly by typing the ref as React.ElementRef<C> rather than a fixed HTML element type. Fourth, provide a default element such as span so the generic falls back gracefully when as is omitted. Fifth, optionally mention extracting a reusable PolymorphicProps<C, Props> utility so the pattern scales across Button, Link, and Typography primitives.

The mistakes people make

A naive implementation types as as a plain string which lets users pass href to a p tag without a type error. Another red flag is using any for the ref or spreading props without generic constraints, which erases autocompletion and disables IDE IntelliSense. Some candidates suggest runtime prop filtering instead of compile-time type omission, showing they confuse validation with type safety. Finally, forgetting to omit your own props from the generic component props causes unresolvable type intersections when color exists both in your theme API and in standard HTML attributes.

What usually comes next

The interviewer may ask how you would extend this to accept custom React components and not just HTML tags, which requires checking that the passed component satisfies the expected props interface. They might also ask how you prevent certain elements from being used, such as blocking div inside a Typography component, which can be done with a more restrictive generic constraint. Another common follow-up is how you handle displayName and defaultProps in a generic forwardRef component, or how tree-shaking and bundle size are affected by exporting many polymorphic primitives.

A concrete example

Imagine a Typography component. When as is h1, TypeScript should allow id and className but reject disabled. When as is a custom Link component, it should surface Link-specific props such as to or replace while still restricting invalid HTML attributes. A senior candidate would define a type where C extends React.ElementType, then use Omit on React.ComponentPropsWithoutRef<C> to strip keys that overlap with the component own props, and intersect the result with those own props. They then wrap the implementation in React.forwardRef and type the ref as React.ElementRef<C>. This gives consumers full autocomplete for whichever element they choose while keeping the design system API strictly typed.

Interview question

Which approach correctly gives a polymorphic Typography component type-safe props so that disabled is rejected when as is h1 but to is accepted when as is a custom Link?

  • a.constrain the generic C to keyof JSX.IntrinsicElements and type the remaining props as ComponentPropsWithoutRef<C>
  • b.type the as prop as string and spread all received props onto the element
  • c.filter out invalid keys at runtime with a prop filtering utility before spreading them to the element
  • d.constrain the generic C to React.ElementType and intersect Omit<ComponentPropsWithoutRef<C>, 'color'> with the component's own propsCorrect
Why?

Option D is correct because React.ElementType accepts both intrinsic tags and custom components, ComponentPropsWithoutRef<C> surfaces the correct props for each C, and Omit resolves clashes with custom APIs like color. Option A is tempting but excludes custom components such as Link, while A and D sacrifice compile-time validation.

Just read this? Test yourself on what you have been reading.

Read the original → blog.logrocket.com

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on typescript — each one lists the topics its interview covers.

See open roles