Use Types vs. Interfaces

Rares Matei
InstructorRares Matei

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 4 years ago

Type aliases and interfaces are often used interchangeably in TypeScript, the decision for using one or the other coming down to readability/conciseness. And they do share a lot of functional similarities too: as with interfaces, types can extend other types (by the use of the intersection operator), and they can also be implemented from or extended from by a class. But they also differ in an important way: your classes can’t implement and your interfaces cannot extend a union of types.

Instructor: [00:00] Most of the times, type aliases in TypeScript are used to alias a more complex type, like a union of other types, to a reusable name. On the other hand, interfaces are used for more traditional object-oriented purposes, where you define the shape of an object and then use that as a contract for function parameters or for classes to implement.

[00:22] This is probably one of the more fundamental differences between types and interfaces. Type aliases and interfaces are also very similar in a variety of ways. Here, I have an interface for an animal and a type that's aliasing the shape of this object to this name.

[00:38] These two types are mostly equivalent in the sense of, if I have a variable that's typed to my interface, and I have another variable that's typed to my type alias, I can assign one to the other without TypeScript complaining.

[00:52] That's because TypeScript uses structural typing. As long as these two types have the same structure, it doesn't really matter that they are distinct types. I can prove this by changing the shape of one of these types. If I change this property, you'll notice TypeScript is now complaining.

[01:09] Type aliases, as per the name, can act as an alias for a more complex type, like a function in this case, or an array. I can also build the equivalent of this type using an interface. These two types are now functionally equivalent. I can also build the equivalent of this type using an interface.

[01:33] While this type is missing all the array methods that this type has, it still works pretty much like an array, as it's saying that all of its keys will be numbers, and the values they're pointing to will be strings.

[01:46] While this is an example of how similar interfaces and type aliases can be, it's also an example of a case where it's more concise to use type aliases. With type aliases, I can express a merge of different other types by means of intersection types.

[02:05] Here, I've created a type, cat, that's both a pet and a feline. If I create a new variable of this type, and I try to access properties on it, I can see it's giving me properties from both pets and felines.

[02:17] If I wanted to do this with an interface, although possible, I'd have to create a totally new interface to express that merge. This new interface has to extend both pet and feline. If I try to create a new variable of this type, and I try to access properties on it, again, I get both properties.

[02:35] Functionally, they're exactly the same here as well. However, the difference is a bit more lexical. There's also no difference between type aliases and interfaces when it comes to using them interchangeably.

[02:48] An interface can extend both an interface and a type. A type can be an intersection of both an interface and another type. Even a class can implement both an interface and a type.

[03:03] One of the biggest functional differences between type aliases and interfaces is that while with the type, I can have a union of multiple other types -- in this case, this is a type that can either be a dog or a cat -- with interfaces, this concept is not really possible.

[03:20] If I try to extend this union type, I'll see that TypeScript complains. That's because an interface is a specific contract. You can't have it be one thing or the other. It must be locked in at declaration. Same goes for a class implementing one of these union types. TypeScript is going to complain, because a class is a blueprint for how to create instances of objects. It must be specific.

[03:43] Finally, another functional difference between interfaces and type aliases is if I have two interfaces with the same name, when used, they will be merged together. If I try to access some properties on foo now, I'll get both A and B.

[03:57] This is something that types do not support. If I try to convert this to a type, I'll get an error saying that it's a duplicate identifier.

[04:05] Now, let's consider this example, importing jQuery. I'm trying to extend it by adding a new function to it. If I try to use that function here, I'll get an error saying that this function doesn't exist on jQuery. That's because it rightfully doesn't exist on the default jQuery interface, as I've just added it.

[04:25] Now, I don't really want to touch the library itself. I would really like to just extend it locally. To do that, I'll go into this different file, which is in the same folder, and I'll say that as part of the jQuery interface, I want to add this new method.

[04:42] If I go back to my file, I'll see that it now passes completion. That's because of this behavior, where TypeScript merges the declaration of interfaces with the same name. However, if the library author didn't mark jQuery as an interface in its typings file, I would not be able to do something like this.

[05:00] As library authors, it's especially important to mark your public APIs as interfaces versus type aliases.

[05:07] To recap, we've looked at how interfaces and type aliases are very similar. Both of them can refer to types that declare the structure of something. They also both support merging of different other types, either via the intersection operator for type aliases, or the extend keywords for interfaces.

[05:26] You can also use them interchangeably. Interfaces can extend from other interfaces and types. Types can be a combination of interfaces and types. Classes can implement types and interfaces.

[05:40] However, you can't really do this if the type you're implementing is a union of one type or the other. That's because both interfaces and classes need to have a shape locked down at the moment of declaration.

[05:54] Finally, if you're a library author, it's useful to expose your public types as interfaces, to allow your consumers to extend them if needed.

~ 4 months ago

Cool, thanks! :)