LogicLoop Logo
LogicLoop
LogicLoop / clean-code-principles / 7 Crucial TypeScript Mistakes Junior Developers Make with Polymorphic Components
clean-code-principles June 2, 2025 4 min read

7 Crucial TypeScript Mistakes Junior Developers Make When Creating Polymorphic Components

Marcus Chen

Marcus Chen

Performance Engineer

7 Crucial TypeScript Mistakes Junior Developers Make with Polymorphic Components

Creating polymorphic components in TypeScript is a common challenge that many junior developers face in their career. When your code does not work as expected, it can be frustrating and may even impact your progress as a developer. In this article, we'll explore how to properly implement a button/link polymorphic component in TypeScript, avoiding the career-destroying mistakes you must avoid.

The Problem with Polymorphic Components

One of the common mistakes junior developers make is incorrectly typing polymorphic components. Let's examine a typical scenario: creating a component that can function as either a button or an anchor link depending on the props passed to it.

TYPESCRIPT
// Initial problematic approach
type ButtonOrLinkProps = React.ButtonHTMLAttributes<HTMLButtonElement> | 
                        React.AnchorHTMLAttributes<HTMLAnchorElement>;

const ButtonOrLink = (props: ButtonOrLinkProps) => {
  if (props.href) {
    return <a {...props} />;
  }
  return <button {...props} />;
};
1
2
3
4
5
6
7
8
9
10

This approach seems logical at first glance, but when you turn on TypeScript, you'll encounter a massive error. The issue is that TypeScript isn't properly disambiguating the union type. Inside the component, checking for the presence of 'href' isn't enough to narrow down the type correctly.

Why Your Code Does Not Work: Understanding Type Narrowing

The root cause of this issue is that TypeScript can't guarantee that 'href' will be present even when dealing with an anchor element - it could be optional. This is a busy mistake that many developers make when trying to progress their career with TypeScript.

Button or anchor component with conditional rendering based on props
Button or anchor component with conditional rendering based on props

When a user doesn't explicitly pass an 'href' prop to an anchor, TypeScript can't narrow down whether the props object is for a button or an anchor. This is one of the developer mistakes that can lead to confusing type errors.

Solution 1: Making href Required for Anchors

One approach to fix this issue is to make the 'href' property required for anchor elements. This helps TypeScript properly narrow down the type.

TYPESCRIPT
// Improved approach with required href
type ButtonOrLinkProps = React.ButtonHTMLAttributes<HTMLButtonElement> | 
                        (React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string });

const ButtonOrLink = (props: ButtonOrLinkProps) => {
  if ('href' in props) {
    return <a {...props} />;
  }
  return <button {...props} />;
};
1
2
3
4
5
6
7
8
9
10
Making href a required string property for proper type narrowing
Making href a required string property for proper type narrowing

By making 'href' a required property for anchor elements, we've helped TypeScript understand that when 'href' is present, we're dealing with an anchor element. This prevents one of the mistakes that prevent self-taught developers from landing a job - improper type handling.

Solution 2: Using Discriminated Unions

While the first solution works, it still has limitations when it comes to properly typing event handlers. A more robust approach is to use discriminated unions with an 'as' prop to explicitly indicate the component type.

TYPESCRIPT
// Using discriminated unions with 'as' prop
type ButtonOrLinkProps =
  | (React.ButtonHTMLAttributes<HTMLButtonElement> & { as?: 'button' })
  | (React.AnchorHTMLAttributes<HTMLAnchorElement> & { as: 'a', href: string });

const ButtonOrLink = (props: ButtonOrLinkProps) => {
  if (props.as === 'a') {
    return <a {...props} />;
  }
  return <button {...props} />;
};
1
2
3
4
5
6
7
8
9
10
11

This approach creates a discriminated union where the 'as' property serves as the discriminant. When 'as' is set to 'a', TypeScript knows we're dealing with anchor props. When it's 'button' (or undefined, making button the default), TypeScript knows we're dealing with button props.

Proper Event Typing with Discriminated Unions

One of the benefits of using discriminated unions is that event handlers get properly typed. This is crucial for avoiding mistakes junior developers make when working with TypeScript.

TYPESCRIPT
// Usage with proper event typing
<ButtonOrLink 
  as="button" 
  onClick={(e) => {
    // e is correctly typed as React.MouseEvent<HTMLButtonElement>
    console.log('Button clicked');
  }}
/>

<ButtonOrLink 
  as="a" 
  href="/some-path" 
  onClick={(e) => {
    // e is correctly typed as React.MouseEvent<HTMLAnchorElement>
    console.log('Link clicked');
  }}
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

With this approach, the event object in onClick handlers is correctly typed based on whether we're using a button or an anchor. This level of type safety is essential for preventing bugs in your application.

Consider simpler solutions before implementing complex polymorphic components
Consider simpler solutions before implementing complex polymorphic components

When to Use Polymorphic Components (And When Not To)

While polymorphic components can be powerful, they come with complexity. One of the career-destroying mistakes you must avoid is overengineering your solutions. Before implementing a highly polymorphic component, consider these alternatives:

  • Create separate components for different element types
  • Use composition instead of polymorphism
  • Consider if the added complexity is worth the flexibility gained
  • Evaluate if your use case truly requires polymorphism

As the saying goes in development, "Rethink your life choices" before implementing overly complex polymorphic components that can take in many different element types.

Conclusion: Avoiding Common TypeScript Mistakes

Properly implementing polymorphic components in TypeScript requires understanding type narrowing and discriminated unions. By avoiding these common mistakes that prevent self-taught developers from landing a job, you'll write more maintainable and type-safe code.

  1. Don't rely on optional properties for type narrowing
  2. Use discriminated unions with explicit type markers
  3. Make properties required when it makes sense for your component
  4. Consider simpler alternatives to complex polymorphic components
  5. Ensure event handlers are properly typed
  6. Test your components with TypeScript's strict mode enabled
  7. Document your component API clearly for other developers

By following these guidelines, you'll avoid the frustration of "my code does not work" moments and build a stronger foundation for your career as a developer. Remember that clean, type-safe code is an investment in your future as a professional developer.

Let's Watch!

7 Crucial TypeScript Mistakes Junior Developers Make with Polymorphic Components

Ready to enhance your neural network?

Access our quantum knowledge cores and upgrade your programming abilities.

Initialize Training Sequence
L
LogicLoop

High-quality programming content and resources for developers of all skill levels. Our platform offers comprehensive tutorials, practical code examples, and interactive learning paths designed to help you master modern development concepts.

© 2025 LogicLoop. All rights reserved.