Frontend Development 10 min read

Improving TypeScript Type Reuse and Component Props in a UI Library

This article examines common TypeScript type‑reuse issues in a component library, compares type aliases and interfaces, demonstrates using utility types like Omit and Pick, introduces unified base props, shows tuple usage for hooks, and explains function overloads to improve code maintainability and type safety.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Improving TypeScript Type Reuse and Component Props in a UI Library

Preface

Recently, while developing a component library, I noticed team members resisting the use of TypeScript, claiming it was too cumbersome. After reviewing the code, I discovered several type‑reuse problems and will share findings and solutions.

1. Insufficient Type Reuse

Code reviews revealed many duplicated type definitions, reducing reuse. Many developers were unclear about the differences between type and interface and how to reuse them.

type definitions can be reused via intersection types ( & ), while interface definitions can be extended with extends . Both can also be mixed. Example snippets:

type Point = { x: number; y: number; };

type Coordinate = Point & { z: number; };
interface Point { x: number; y: number; }

interface Coordinate extends Point { z: number; }
type Point = { x: number; y: number; };

interface Coordinate extends Point { z: number; }
interface Point { x: number; y: number; }

type Coordinate = Point & { z: number; };

2. Adding Only New Properties When Reusing

Team members often recreated a type to drop unwanted properties instead of using utility types. For example, they redefined Props1 to omit property c from Props .

interface Props { a: string; b: string; c: string; }

interface Props1 { a: string; b: string; e: string; }

Using Omit or Pick makes this more efficient:

interface Props { a: string; b: string; c: string; }

interface Props1 extends Omit
{ e: string; }
interface Props1 extends Pick
{ e: string; }

3. Inconsistent Base Types Across Components

Different components use inconsistent prop names such as show , open , or visible . Defining a unified base type improves consistency and maintainability.

import { CSSProperties } from 'react';

type Size = 'small' | 'middle' | 'large';

type BaseProps
= {
  /** Custom class name */
  className?: string;
  /** Custom style object */
  style?: CSSProperties;
  /** Control visibility */
  visible?: boolean;
  /** Component size */
  size?: Size;
  /** Disabled state */
  disabled?: boolean;
  /** Read‑only state */
  readOnly?: boolean;
  /** Default value */
  defaultValue?: T;
  /** Current value */
  value?: T;
  /** Change handler */
  onChange: (value: T) => void;
};

Component‑specific props then extend this base:

interface WInputProps extends BaseProps
{
  /** Max length of input */
  maxLength?: number;
  /** Show character count */
  showCount?: boolean;
}

4. Arrays with Heterogeneous Elements

When a custom hook returns two values, the team preferred returning an object using any[] , which loses type safety. Using a tuple preserves distinct types.

function useMyHook(): [string, number] {
  return ['示例文本', 42];
}

function MyComponent() {
  const [text, number] = useMyHook();
  console.log(text); // string
  console.log(number); // number
  return null;
}

5. Functions with Variable Arguments and Return Types

Developers often resort to any for parameters and return values. Function overloads provide a type‑safe way to handle multiple signatures.

function greet(name: string): string;
function greet(age: number): string;
function greet(value: any): string {
  if (typeof value === "string") {
    return `Hello, ${value}`;
  } else if (typeof value === "number") {
    return `You are ${value} years old`;
  }
}

For arrow functions, a callable type can emulate overloads:

type GreetFunction = {
  (name: string): string;
  (age: number): string;
};

const greet: GreetFunction = (value: any): string => {
  if (typeof value === "string") {
    return `Hello, ${value}`;
  } else if (typeof value === "number") {
    return `You are ${value} years old.`;
  }
  return '';
};

6. Choosing Between type and interface for Component Props

Both can define props, but interface supports declaration merging, allowing modules to extend component props via declare module . Therefore, using interface is recommended for extensibility.

interface UserInfo { name: string; }
interface UserInfo { age: number; }

const userInfo: UserInfo = { name: "张三", age: 23 };

Conclusion

TypeScript is not difficult; understanding its powerful features—type reuse, utility types, tuples, overloads, and proper use of type vs interface —greatly improves code quality and developer experience.

frontendTypeScriptbest practicescomponent libraryUtility Typesinterface{}type reuse
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.