Let's resolve this forever outstanding question:
Should I use type
or interface
?
Spoiler: It's called "TypeScript" not "InterfaceScript" ๐
Why a resolution is needed?
Typescript documentation is itself vague about which one should be used. However, vagueness is not helpful while creating a reliable software.
Most programming languages contain good parts and bad parts. I discovered that I could be a better programmer by using only the good parts and avoiding the bad parts. After all, how can you build something good out of bad parts?
Douglas Crockford
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Zen of Python
type - Good, interface - Bad
1. interface
is mutable
Redeclaring an interface appends to existing definition:
interface MyInterface {
name: string
}
interface MyInterface {
phone: number
}
let variable : MyInterface = {
name: 'MyName',
phone: 123456789
}
This opens a can of worms (bugs)! In a project of significant size. Types are declared in several files, and some types are declared globally. However, if interface definition can be extended in any file, users cannot be really sure what the type is going to be at usage. Also, these bugs can be extremely challenging to locate.
On the other hand type
s can only be created once:
// Error: Duplicate identifier 'MyType'.
type MyType = {
name: string
}
type MyType = {
phone: number
}
Mutability of interface
s is a bad feature: Value proposition of Typescript is to provide guard rails to validate types of variables in your program. Utility of Typescript is compromised if a programmer cannot be sure of what the type of interface is going to be at usage.
2. interface
has unusual syntax
type
s naturally extend the syntax of declaring variables in JavaScript while interface
s have their own syntax which causes needless complexity.
// Variable creation rule
// Construct Name = Value;
var x = 1;
let y = 2;
const z = 3;
// Types creation rule
// Construct Name = Value;
type MyType = string | number;
// Interface creation rule
// Construct Name {Value}; ๐ค
interface MyInterface {
name: string
}
3. extends
on double duty
extends
in Typescript has dual purpose:
- Extending an interface: More properties can be added to an
interface
usingextends
keyword.
interface Animal {
name: string
}
interface Bear extends Animal {
honey: boolean
}
- Conditional type checking: For inputs to a
Generic
extends
keyword is used to validate type at usage. Additionally, for conditional typesextends
keyword functions as a condition checker for a type being subset of another type.
const firstChar = <T extends string>(input: T) => {
return input[0];
}
// Works!!
firstChar('abc')
// Error: Argument of type 'string[]' is not assignable to parameter of type 'string'.
firstChar(['abc'])
The dual usage for keyword extends
is a source of lots of confusion. It makes it difficult for new users to adapt advanced TypeScript features.
However, we can do better! By not using interface
at all we can remove this confusion as extends
will be left with only one usage.
PS: Special thanks to Ivรกn Ovejero for writing TypeScript and Set Theory
4. interface
only works with objects
Now what's up with that ๐คทโโ๏ธ. In my opinion this is the origin of problem. interface
was designed to make it easier to work with objects, which resulted in creation of two almost similar features and endless conversation of "type vs interface".
Software languages, libraries and frameworks follow principles of evolution. Like in any evolutionary system many species and initiatives take place to address specific needs and fill certain gaps.
However, not all initiatives successfully manage to address real problems, and sometimes can have detrimental effects on overall structure of program. Unfortunately, unsuccessful experiments cannot be removed from a language due to backwards compatibility.
It is rarely possible for standards committees to remove imperfections from a language because doing so would cause the breakage of all of the bad programs that depend on those bad parts.
Douglas Crockford
But we as developers can separate good parts from bad parts and only use good parts to make our softwares great!
Top comments (16)
As for
interface
mutations. It is not amutation
. You can only add new properties but not remove. Apart from that, you can extendinterface
only in a scope of one module (read file). Once you addexport
to your file - you will be unable to extend this interface in another file.interface
in fact is much safer thantype
. I would say, that in most cases, you should consider usinginterfaces
.As for performance and intersections, please see typescript wiki page
They suggest to use
interface
withextends
overtype intersection
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
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:
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.
Because
Record
is indexed andinterface Animal
is not. I am not sure why are you talking about inconsistent code, tech debt andThe 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 feelingsIsn'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.
This is perfectly fine:
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.
I've been wandering between
type
andinterface
for years, I've settled forinterface
for years but now I favortype
instead since it's much more flexible and can do the same as well. Just a few examples that can only be achieved withtype
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 thaninterface MyType extends Bar, Baz {}
(read officatial performance docs: github.com/microsoft/TypeScript/wi...)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
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.
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?
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.
Please see typescript official wiki page
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.
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.
Trying to impose python ways on a typescript/javascript ecosystem is not practical. It's like trying to impose communism on the US.
Thanks :)