The Difference between TypeScript Interfaces and Types

Johnny Simpson - Sep 18 '22 - - Dev Community

In TypeScript, you might have noticed you can declare custom types in two different ways. One is with the interface keyword, and the other is with the type keyword. As such, you may find yourself wondering why there are two ways to do one thing - and you're not alone. I already covered in my guide to declaring custom types in TypeScript, how you can use interfaces and types - but let's look a little bit more into how they differ.

1. Interfaces extend syntax is different to types

If we define a type in TypeScript, it is not extendable after the fact. For example, consider this custom type I just made up:

type user = {
    name: string,
    age: number
}
Enter fullscreen mode Exit fullscreen mode

If, after its been defined, I suddenly realise I want to add an address too, I can do this using the following syntax:

type userWithAddress = user & {
    address: string
}
Enter fullscreen mode Exit fullscreen mode

With interfaces, we can do the same, but the syntax remains slightly different:

interface user {
    name: string;
    age: number;
}
interface userWithAddress extends user {
    address: string
}
Enter fullscreen mode Exit fullscreen mode

Now userWithAddress contains all the properties of user, plus one additional property - that being address.

The only difference between these two ways of extending types is how they handle conflicts. For example, if you extend an interface and mention a property that has already been defined, an error will be thrown. For example, this will not work:

interface user {
    name: string;
}
interface newUser extends user {
    name: number;
}
Enter fullscreen mode Exit fullscreen mode

Meanwhile, with type, you can do this:

type user = {
    name: string
}

type newUser = user & {
    name: number
}
Enter fullscreen mode Exit fullscreen mode

While this will not throw an error, it may result in some unexpected results - so you should avoid it where necessary. For example, above, the name property is reduced to type never - since a type can never be both string and number at the same time. :)

2. Interfaces can be merged - types cannot

Along the same lines, types cannot be merged, while interfaces can if you declare them multiple times. For example, if we have a type, we can't do something like this:

type cat = {
    name: string
}
type cat = {
    color: string
}
Enter fullscreen mode Exit fullscreen mode

In fact, the above code will throw an error. Meanwhile, with interface, we can do that - and it'll merge both declarations. So the example below will create a type called cat with both name and color properties:

interface cat {
    name: string;
}
interface cat {
    color: string;
}
Enter fullscreen mode Exit fullscreen mode

3. Interfaces cannot extend a primative

While we can create a type which can be an alias for a primitive type like string, interface cannot do this. For example, if you want to create a type called myName, which is always of type string, we can do it like this:

type myName = string;
Enter fullscreen mode Exit fullscreen mode

Here, myName becomes an alias for string - so we can write myName instead of string, essentially, anywhere. Meanwhile, interface does not have this ability. The following cannot and will not work:

interface myName extends string {

}
Enter fullscreen mode Exit fullscreen mode

4. Types can create unions, while interfaces cannot

We can create union types with the type keyword, but we can't do that with an interface. For example, here, userId can be a string or number:

type userId = string | number
Enter fullscreen mode Exit fullscreen mode

Meanwhile, the above cannot be achieved with an interface, since interface defines the shape or type of an object.

Conclusion

As you can see, the main differences between type and interface kind of depend on the circumstances you use them in. Almost all the features of interface are available in type, which means you might find yourself more frequently going to type. Typically, though, it tends to be based on preference or what works best in your codebase.

In any case, rest assured that it isn't as confusing as you might first think - with both interface and type effectively being two ways to do the same thing!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .