Reputation: 3556
This question is not a dupe of this one, despite the similarites in the title. I'm not asking about the differences but the implications of the differences. Also, I've considered the risk of being opinion-based and voided it by limiting to two viable options and specifying a set of conditions.
I asked a question and got a type based answer, which worked.
export type Configs = {
[key: string]: ButtonConfig | TextBoxConfig | CheckConfig;
}
When I applied that in my project, the IDE TSLint'ed a sugestion to apply interface based approach instead.
export interface Configs {
[key: string]: TextBoxConfig | ButtonConfig | CheckConfig;
}
Those are two fundamentally different approaches and I got curious as to which would be most appropriate and when, according to best practices. A set of blogs like this one offer comparison tables. However, I'm confused as to the implication of said differences. For instance: "allows the creation of the new name for a type" is hard to evaluare versus "provides the powerful way to define entities", from my point of view. It's like saying "this one is large" and "that one has bananas".
I've found blogs that contradict and correct other blogs and docs, too. There are questions on SO too but those I've found, list the deviations rather than elaborating on the implications thereof. All in all, it's unclear to me how to interpret the info base.
The original poster, @thorjacobsen, replied that in his team they went for type based because of semantical preference, which is a local reason, not guaranteed to be applicable in a general case.
Of what I gathered, I get a sense that (unless other requirements impose a choice), interface is preferred by teams coming from .NET (keywork familiarity) while type is preferred by team with JS experience (historial sentiment). NB, I'm speculating here.
Given that times change and the methodics adapts to the wind of time, I wonder which one (if any) of the two would be suggested in a general case as the best practice (or safest bet until more info emerges).
Upvotes: 1
Views: 1160
Reputation: 152890
First, the facts: this answer and this one with examples are absolutely correct.
The only thing interfaces offer over types is declaration merging. On the other side, types can do a lot more than interfaces (mapped types, conditional types, etc).
Now, you understandably wonder why the language was designed that way:
What confuses me in the information I've gathered is that there are two, virtually equivalent approaches to achieve the same kind of goal. It seems that it's (almost) a matter of taste and computer languages are not created that way. We pick the most optimal approach and make it default, if possible. In other words, there's no point introducing feature x and there's already feature y that's doing the same thing.
I'll try to answer this as well as I can by giving some background on the history of TypeScript.
From the very beginning TypeScript has had interfaces as a way to define the named type of an object or function:
interface Foo {
bar: number;
}
However, in some situations you didn't necessarily have to specify an interface for TypeScript to understand the structure of an object. Take this example:
let foo = {
bar: 42
}
The compiler will infer that foo
has the type { bar: number }
. It's even possible to manually annotate the type without creating an interface:
let foo: { bar: number } = {
bar: 42
}
This shows that it was always possible to create complex types without interfaces. There was just no way to put a name to them as to make them more readable and re-usable.
Then TypeScript 1.4 introduced type aliases. The new type
keyword allows to define an alternative name for any kind of type. They work for interfaces:
MyFoo = Foo
But also for types which previously couldn't be defined using a name:
type MyNumberArray = number[];
type MyFunction = (arg: string) => void;
Of course this now meant we can also define our interface Foo
only using type
:
type Foo = {
bar: number;
};
Since TypeScript 1.6, type aliases could be generic as well allowing them to describe all possible type constructs known to TypeScript.
To summarize: Types are designed to be type aliases, a way of giving any type a name. This includes any type that an interface can represent.
Which one to use is a matter of personal taste. There is no right or wrong, except that it's important to be consistent. The following are a few options how one could decide to go about this (I'm ignoring declaration merging here because in most projects it's not used)
Of course there are many more ways to do it, and you'll have to find something that suits your needs.
Upvotes: 4
Reputation: 3164
Different people can have different approaches. One possible and very simple approach:
Use type aliases to describe the exact shape of a custom piece of data.
Use interfaces to describe the exact shape of a custom piece of functionality related to class or function signature.
This is not a line drawn in sand and the distinction between the two can easily get blurred. For example, because type alias is an alias, it can be used to alias nearly everything including a piece of functionality (like a function) providing another name e.g. alias for a specialized functionality and so forth. Still, having some simple initial distinction is helpful in my view.
Upvotes: 1