Type or Interface for TS projects ?

#javascript#typescript
18 Sep 2024

Why You Should Use type Instead of interface in TypeScript

TypeScript provides two main ways to define the shape of an object: type and interface. While both can be used interchangeably in many cases, there are specific scenarios where type offers more flexibility and power. This blog will explore these scenarios and explain why you might prefer type over interface.

Key Differences Between type and interface

1. Union Typestype can define union types, which interface cannot.

1 type Result = Success | Failure;

2. Intersection Typestype can define intersection types.

1 type Combined = TypeA & TypeB;

3. Primitive Typestype can alias primitive types.

1 type StringAlias = string;

4. Tuplestype can define tuples.

1 type Tuple = [number, string];

5. Mapped Typestype can create mapped types.

1 2 3 type Readonly<T> = { readonly [P in keyof T]: T[P]; };

6. Complex Type Constructstype can define more complex type constructs like conditional types.

1 type Conditional<T> = T extends string ? string : number;

Let's consider a React component where we define the props using type instead of interface.

1. Union Types

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // Case type type ButtonProps = { label: string; onClick: () => void; variant: 'primary' | 'secondary'; }; const Button: React.FC<ButtonProps> = ({ label, onClick, variant }) => { return ( <button className={`btn-${variant}`} onClick={onClick}> {label} </button> ); }; // Case interface // This is not possible with interface interface ButtonProps { label: string; onClick: () => void; variant: 'primary' | 'secondary'; // Error: String literal types are not allowed in interfaces }

2. Intersection Types

When you need to combine multiple types, type is more flexible. Using type

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type BaseProps = { id: string; }; type AdvancedProps = { isActive: boolean; }; type CombinedProps = BaseProps & AdvancedProps; const Component: React.FC<CombinedProps> = ({ id, isActive }) => { return ( <div id={id} className={isActive ? 'active' : ''}> Content </div> ); };

Using interface (More Verbose)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface BaseProps { id: string; } interface AdvancedProps { isActive: boolean; } interface CombinedProps extends BaseProps, AdvancedProps {} const Component: React.FC<CombinedProps> = ({ id, isActive }) => { return ( <div id={id} className={isActive ? 'active' : ''}> Content </div> ); };

3. Tuples

When you need to define a tuple type, type is the only option.

Using type

1 2 3 4 5 6 7 8 9 10 11 type TupleProps = { coordinates: [number, number]; }; const CoordinateDisplay: React.FC<TupleProps> = ({ coordinates }) => { return ( <div> X: {coordinates[0]}, Y: {coordinates[1]} </div> ); };

Using interface (Not Possible)

4. Mapped Types

When you need to create a mapped type, type is more suitable.

Using type

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type ReadonlyProps<T> = { readonly [P in keyof T]: T[P]; }; type UserProps = { name: string; age: number; }; type ReadonlyUserProps = ReadonlyProps<UserProps>; const UserComponent: React.FC<ReadonlyUserProps> = ({ name, age }) => { return ( <div> Name: {name}, Age: {age} </div> ); };

Using interface (Not Possible)

1 2 3 4 // This is not possible with interface interface ReadonlyProps<T> { readonly [P in keyof T]: T[P]; // Error: Mapped types are not allowed in interfaces }

Conclusion

While interface is useful for defining object shapes that are intended to be extended or implemented by classes, type offers more versatility and power for defining complex types. By understanding the strengths of type, you can make more informed decisions in your TypeScript projects.

In summary, use type when you need:

  • Union types
  • Intersection types
  • Primitive type aliases
  • Tuples
  • Mapped types
  • Complex type constructs like conditional types
  • This flexibility makes type a better choice in many scenarios.