DEV Community

Cover image for TypeScript: type vs interface

TypeScript: type vs interface

Udayan Maurya on January 23, 2023

Let's resolve this forever outstanding question: Should I use type or interface? Spoiler: It's called "TypeScript" not "InterfaceScript" 😉 ...
Collapse
 
captainyossarian profile image
yossarian

As for interface mutations. It is not a mutation. You can only add new properties but not remove. Apart from that, you can extend interface only in a scope of one module (read file). Once you add export to your file - you will be unable to extend this interface in another file.

interface in fact is much safer than type. I would say, that in most cases, you should consider using interfaces.

interface Animal {
  tag: 'animal',
  name: 'some animal'
}

declare var animal: Animal;

const handleRecord = (obj:Record<string, unknown>) => { }

// Change `interface` to `type` and error will be resolved
const result = handleRecord(animal) // error
Enter fullscreen mode Exit fullscreen mode

As for performance and intersections, please see typescript wiki page
They suggest to use interface with extends over type intersection

// BAD
type Foo = Bar & Baz & {
  someProp: string;
}

// GOOD
interface Foo extends Bar, Baz {
  someProp: string;
}
Enter fullscreen mode Exit fullscreen mode

You need to use type alias mostly for writing type utilities, like mapping and conditional types or declaring unions.

Please see this stackoverflow answer

Indexing was already mentioned, but there is another one thing. Interfaces are eagerly evaluated. See second part of the answer

To summarize:
Interfaces
Use interfaces when you need to declare an object type. In most cases, you should start from interface

Types
Use type aliases for declaring conditional types and utility types

Collapse
 
udayanmaurya profile image
Udayan Maurya

Thanks for your input you do bring about two valid points that interface not being appendable across files and eager evaluation!

However, for the example:

interface Animal {
  tag: 'animal',
  name: 'some animal'
}

declare var animal: Animal;

const handleRecord = (obj:Record<string, unknown>) => { }

// Change `interface` to `type` and error will be resolved
const result = handleRecord(animal) // error
Enter fullscreen mode Exit fullscreen mode

Why do you think it should error?

For the performance issue. I've shared the benchmark I've found here.
Full project level compilation mostly happens in CI and does not affect developer productivity. I'd estimate in most extreme cases intersects can take around 5min more in CI, which is generally not a bottleneck.
However, inconsistent code and tech debt caused by confusion and ambiguity causes more harm to codebase (bugs created) and developer productivity (resolve bugs/add new features).
I'd almost always advocate for coding patterns which reduce ambiguity and can be scaled in large teams of people with varied level of skill-sets.

You should checkout youtube.com/watch?v=NPB34lDZj3E for context.

Collapse
 
captainyossarian profile image
yossarian

Why do you think it should error?

Because Record is indexed and interface Animal is not. I am not sure why are you talking about inconsistent code, tech debt and The Post JavaScript Apocalypse - Douglas Crockford video. I don't think it is relevant. There is an official typescript wiki. But it is up to you which patterns needs to be used in your project. NO hard feelings

Collapse
 
alaindet profile image
Alain D'Ettorre

I've been wandering between type and interface for years, I've settled for interface for years but now I favor type instead since it's much more flexible and can do the same as well. Just a few examples that can only be achieved with type

type ButtonColor = 'primary' | 'secondary' | 'outline';
type TodoItem = { id: string; title: string; done: boolean; };
type CreateTodoItemDto = Omit<TodoItem, 'id'>;
type Person = { name: string; age: number };
type Programmer = Person & { favoriteLanguage: string; };
Enter fullscreen mode Exit fullscreen mode
Collapse
 
retyui profile image
Davyd NRB

one point that was missed Index signature for type 'number|string' is missing in interface

Alos, all arguments about extend nothing until: type MyType = Foo & Bar slower than interface MyType extends Bar, Baz {} (read officatial performance docs: github.com/microsoft/TypeScript/wi...)

Collapse
 
udayanmaurya profile image
Udayan Maurya • Edited

Thanks for sharing the index signature issue. I was not aware of it!

I have ran performance benchmark for 10k intersect vs 10k interface extend. Results

  • Interface extend: 1 min 26 sec 629 ms
  • Type intersect: 2 min 33 sec 117 ms

10k intersections can be possible in a fairly large project. Types take around a minute more to compile. This cost is generally incurred in CI pipelines and not during development. For development cases only concerned types per file are resolved, which should remain instantaneous in most cases.
A few minutes of additional compile time in CI, in more heavy weight cases, is a good trade off to avoid all the gotchas associated with interface.

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Isn't it the case that an object can have many Interfaces but only one Type? The point of interfaces is normally to be able to treat many different object types with the same code for specific functions that use the interface.

Collapse
 
brense profile image
Rense Bakker

This is perfectly fine:

const myObj: SomeType & AnotherType & MoreType = {
  // Use all properties from 3 types
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
miketalbot profile image
Mike Talbot ⭐

Great thanks for that, in my few experiences I've gone the C# style route and used interfaces where there are multiple and class types otherwise.

Collapse
 
sandeepbansal_2 profile image
Sandeep Bansal - 2/100

Great post, thank you for sharing your thoughts and insights

You've raised some valid points about the potential issues with using interfaces, particularly in terms of mutability, syntax, and the confusion surrounding the use of the "extends" keyword.

My question for you would be: In your opinion, are there any situations or use cases where interfaces would still be the preferred choice over types? Are there any benefits to using interfaces that you think are worth considering, even with the potential issues you've outlined?

Collapse
 
udayanmaurya profile image
Udayan Maurya

Short answer: No.
Long answer: Purpose of the post was to resolve ambiguity surrounding type vs interface debate, following Douglas Crokford's Good parts philosophy.

Good parts philosophy: Software languages/frameworks will continue to have bad parts because of failed experiments, and failed experiments cannot be removed to support all the code which has been written using those parts. Therefore, onus of separating good parts from bad parts falls on to developers.
Here is a good video for more: youtube.com/watch?v=NPB34lDZj3E.

Collapse
 
captainyossarian profile image
yossarian
Collapse
 
the_yamiteru profile image
Yamiteru

Nice comparison but I personally wouldn't use words such as good or bad.

If you use such words you really miss the point. Both types and interfaces have their use-cases and for those use-cases they're always "good" (the obvious way to do it).

So what I'd rather see is a list of use-cases with examples of types and interfaces. No comparing which is better or worse.

Collapse
 
brense profile image
Rense Bakker

I think interfaces were meant to be used with class based typescript (oop). In that context its a better known concept. For functional programming I agree type is better.

Collapse
 
miketung profile image
Mike T

Trying to impose python ways on a typescript/javascript ecosystem is not practical. It's like trying to impose communism on the US.

Collapse
 
mystyle2006 profile image
Inho

Thanks :)