Hüsam Yıldırım
Portfolio

Stop Using Interfaces: Here's Why!

Interfaces and type aliases are very similar; Almost all of an interface's functionality are available in a type interface. However, there are some important differences that can impact how you maintain and arrange your code. Let's explore these differences in detail.

If you're still using JavaScript, it's time to think about migrating to TypeScript 🤔

1. Extending Types

When creating a new type that depends on another, you can use the extends keyword with interfaces or the intersection operator & with type aliases.

Using interfaces

interface UserProps {
  name: string
  age: number
}

interface AdminProps extends UserProps {
  role: string
}
// AdminProps: { name: string; age: number; role: string; }

Using Type Aliases

type UserProps = {
  name: string
  age: number
}

type AdminProps = UserProps & {
  role: string
}

Type aliases provide a more concise and readable syntax for extending types.

2. Interface can only describe objects

Using interfaces means you can only describe objects. You can't use them for simple values like strings or numbers, or for combinations of types (unions). On the other hand, type aliases can describe objects, simple values, and anything else.

Using interfaces

interface Address {
  address: string
}

const address: Address = {
  address: '123 Main St',
}

Using Type Aliases

type Address = string

const address: Address = '123 Main St'

3. Using Utility Types

Utility types like Omit and Partial can make your code more reusable and maintainable. However, the syntax can become a bit tricky with interfaces.

Using Type Aliases

type UserProps = {
  name: string
  age: number
  createdAt: Date
}

type GuestProps = Omit<UserProps, 'name' | 'age'>
// GuestProps: { createdAt: Date; }

Using Interfaces

interface GuestProps extends Omit<UserProps, 'name' | 'age'> {}

Type aliases offer a simpler and more straightforward syntax when using utility types.

4. Describing Tuples

When you need to define a collection of values with specific types, type aliases provide a simpler and more direct approach.

Using Type Aliases

type Address = [number, string]

const addresses: Address[] = [
  [1, 'Main St'],
  [2, 'Second St'],
]

Using interfaces

interface Address extends Array<string | number> {
  0: number
  1: string
}

5. Interfaces can be merged?

All the previous points focus on syntax differences, but at the end, both interfaces and type aliases achieve similar goals. The key difference is that interfaces can be merged, while type aliases cannot. But what does that really mean?

interface User {
  name: string
}

interface User {
  age: number
}

// The User interface is merged to include both name and age

// interface User {
//   name: string
//   age: number
// }

Type aliases cannot be merged, and you'll get an error if you try to do so!

Merging interfaces can be useful, especially if you need to override an interface from a third-party library. However, it can also lead to confusion if not handled carefully, particularly when working in a team or writing a library.

6. Familiarity

JavaScript developers are generally more familiar with the type syntax than the interface syntax. The = sign in type aliases feels more natural to developers coming from a JavaScript background.

For example, consider how you might define a simple type in JavaScript using a variable assignment:

const name = 'John Doe'

In TypeScript, using a type alias feels very similar:

type Name = string

const name: Name = 'John Doe'

On the other hand, defining an interface might feel less intuitive:

interface Name {
  name: string
}

const name: Name = {
  name: 'John Doe',
}

The type syntax is more straightforward and aligns well with the conventions JavaScript developers are used to, making it an easier transition for those migrating TypeScript.

Conclusion

As Matt Pocock says, you should use types by default until you need a specific feature of interfaces.

References