Interfaces vs Types in Typescript. What to pick and why?

Choosing the right one definitely helps!

🤔 Interface vs Types

Typescript supports both interfaces and types. Ok, what’s the problem?

They kind of solve similar problems. You can use interfaces to structure the data. You can also use types to structure the data.

Because of this similarity, there often comes a question as to what needs to be used and when.

Although this has been discussed quite a lot of times over the internet (Reddit, stack overflow, Facebook groups, Discord chats, …), I will try to summarize this article with comparisons for quick understanding.

1. Syntax

Obviously, they differ in the way we write them.

Type

Interface

2. Extending/Implementing

  • interface allows you to extend other interfaces using the extends keyword:

Use interface when you want to create a contract that other interfaces can extend to add or modify properties/methods. This is especially useful when defining interfaces for object shapes where you want to enforce a specific structure and extend it for different use cases.

  • type allows you to create union or intersection types using & and |:

Use type when you need to create complex type compositions, such as combining multiple types using union or intersection operators. This is particularly useful for creating flexible type definitions for functions and variables.

3. Compatibility

  • interface is open to merging:

  • type does not support merging:

Use type when you want to define standalone, self-contained type aliases. This can help prevent unintentional naming conflicts.

Implications:

  • Merging: interface allows merging multiple declarations with the same name. This can be useful for gradual development and code organization. However, it may also lead to accidental merging if not used carefully.

  • Isolation: type provides more isolation since it doesn't merge declarations. This can lead to better encapsulation and reduced risk of naming conflicts.

4. Declaration vs. Expression

  • interface is limited to defining object shapes and methods but cannot be used as a general type alias:

Use interface when you want to define the shape of objects, classes, or interfaces.

  • type can represent a wide range of types:

Use type when you need to create more complex and dynamic type definitions, such as aliasing primitive types, tuples, union types, or intersection types.

Implications:

  • Expressiveness: type provides greater expressiveness and flexibility for defining complex types and aliases. This can be advantageous when dealing with intricate type scenarios.

5. Immutability

  • interface properties are mutable by default:

Use interface when you want to define mutable object structures.

  • type properties can be made readonly using the readonly modifier:

Use type when you want to define immutable types for objects.

6. Literal Types

  • interface can define index signatures with specific literal types:

Use interface when you need to work with dynamic keys and specific literal values like "red" | "green" | "blue" in an index signature. This is particularly useful for scenarios where you have a known set of possible values.

Use type when you want to create literal types like string literals, numeric literals, or boolean literals:

7. Index Signatures

  • interface supports index signatures to define dictionary-like objects:

Use interface when you need to define objects with dynamic keys, such as dictionaries or associative arrays.

  • type also supports index signatures:

Use type when you require index signatures to work with dynamic key-value pairs.

8. Compatibility with Classes

  • interface can be implemented by classes using the implements keyword:

Use interface when you want to define a contract that classes must adhere to.

  • type cannot be implemented by classes.

Use type when you need to define complex type structures but don't require class implementation, such as function type aliases.

Implications:

  • Class Contracts: If you need to enforce a contract on class implementations, interface is the appropriate choice. However, if you need to define complex type structures unrelated to class implementations, type is more versatile.

9. Built-in Utility Types

TypeScript provides built-in utility types that work seamlessly with both type and interface. These utility types are used to manipulate and transform types to make development more efficient. Some commonly used utility types include:

  • Partial: Creates a new type with all properties of T set to optional. This is useful when you want to work with a subset of the properties of an interface.

  • Partial: As with interface, Partial creates a new type with all properties of T set to optional. This is useful when working with type definitions.

10. Preference for Readability

Ultimately, your choice between type and interface should prioritize readability and maintainability for your specific use case and coding style. There's often no strict rule, and personal/team conventions may play a role in your decision.

In summary, interface is generally preferred for defining object shapes, class contracts, and when working with built-in utility types. On the other hand, type is more versatile, making it suitable for creating complex types and type aliases, especially when dealing with union or intersection types. Your choice should align with the specific needs of your project and coding practices.

Reply

or to participate.