9

I see a lot of TypeScript developers overuse interface. In fact they use it for almost everything even if their code is more functional than object oriented. Personally I prefer type which is more flexible and doesn't confuse if the interface is implemented by any class or it's used just to define object type. Are there any advantage to use interface over type or is it some kind of legacy thing that developers get used to do?

1
  • 1
    "even if their code is more functional than object oriented" interface is not inherently either of these. "[type]doesn't confuse if the interface is implemented by any class" let me be clear that is your expectation. Not the expectation of every TypeScript programmers. To me an interface is not in any way bound to classes. The type system and the class system are different things. Functional programmers should understand that because Haskell, among others, has a type system. interface defines a structural expectation, not an inheritance chain. Commented Jun 24, 2020 at 12:03

5 Answers 5

9

The intended use for type is for aliasing of types, especially intersection/union types.

They should not be used like an interface, as the documentation states:

As we mentioned, type aliases can act sort of like interfaces; however, there are some subtle differences.

One difference is that interfaces create a new name that is used everywhere. Type aliases don’t create a new name — for instance, error messages won’t use the alias name. In the code below, hovering over interfaced in an editor will show that it returns an Interface, but will show that aliased returns object literal type.

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
Sign up to request clarification or add additional context in comments.

Comments

5

There is no objective reason to choose interfaces

The TS handbook clearly states that it is a matter of preference. There is no philosophical reason, no intent, no "should" or "shouldn't".

Type aliases and interfaces are very similar, and in many cases you can choose between them freely.

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration.

Programmers who choose interfaces over type aliases do it out of habit, personal preference, or because they read the handbook and saw that last line just bellow:

If you would like a heuristic, use interface until you need to use features from type.

There use to be a reason

The reason why historically people had an objectively good reason to prefer interfaces by default was because classes could not implement a type alias and interfaces could not extend a type alias. This limitation was lifted with this PR.

You can see advice like in this article (paywall) all over the Internet, but as explained above, today they make no sense:

Interfaces can be implemented, extended, and merged, so they’re preferred to type literals.

There remain situational reasons

1- Being able to do declaration merging with interfaces is not a reason to choose interfaces by default, but it has situational use cases, like supporting different versions of Javascript with d.ts files.

This is the case with RegExpMatchArray: depending on the lib you declare in tsconfig you will get one or both declarations, and they will be merged:

// from lib.es5.d.ts
interface RegExpMatchArray extends Array<string> {
    /**
     * The index of the search at which the result was found.
     */
    index?: number;
    /**
     * A copy of the search string.
     */
    input?: string;
    /**
     * The first match. This will always be present because `null` will be returned if there are no matches.
     */
    0: string;
}

// from lib.es2018.regexp.d.ts
interface RegExpMatchArray {
    groups?: {
        [key: string]: string
    }
}

Paired with module augmentation, interface merging is a great a way for users of a library to customize it.

A few caveats though:

  • You never really know what the members of an interface are since it can be mutated;
  • You can inadvertently extend an existing interface when you think you are creating a new one;
  • Interfaces can't extend an index signature unless they have themselves an index signature, because they are open, which is often not what you want and can be supremely annoying.

2- Another technical reason to prefer an interface over an object type is that extending an interface is more efficient than intersecting 2 object literal types. If your objects are big, it can make a difference.

3- I am not convinced by the technical reason cited in Archimedes Trajano's answer: the example used in the supporting article would not even compile, which leads me to believe the author could not think of a single realistic scenario where this inlining would happen, but it is certainly something to consider when d.ts files get incomprehensibly large.

4- Finally, interfaces are great for type-level programming, but this is a completely different discussion.

Comments

4

An interface is a representation of what an entity should be ; specifically objects.

Interfaces are not only existing in Typescript, but in Java, C++ ... With different syntax but with the same meaning.

IMO :

interface is what you should use when describing what an object/class should be.

type in other hands should be considered as an alias maker.

7 Comments

Correction interface is not just for objects. You can have an interface for a function. Yes, "functions are objects" and all that but a function interface and an object interface are definitely different and not interchangeable, nor are then swappable in any direction. For objects the interface describes the shape (expected properties and values), while for functions, the interface describes the signature (expected input and output).
It's a fair point you have made, my sentence specifically objects wasn't clear enough. For functions thought, you would rather use type keyword, because usually you do something like type MyFunc = (foo:string) => void:, that's why I said interfaces are specially made for object/class.
@VLAZ I wasn't aware of this syntax. Interesting! Does it work using function keyword or is it a workaround using the fact that a function is an object ?
It's an alternative syntax for interface. It's not at all related to functions being objects - it's just an overloaded implementation. Honestly, it's a bit annoying but I suppose it was done to have one canonical way of doing both function interfaces and structural interfaces. It could have easily been, say signature Add instead. Unfortunately, the function interface/signature doesn't work with annotating function declarations. At best, you can do function expressions like arrow functions or myFn: Add = function() {}
|
1

I started researching on this after I turned on @typescript-eslint/strict which enabled consistent-type-definitions that defaulted preferring interface.

In my research, I found an article that describes a technical reason for preferring interface and not an opinionated one.

Specifically, the use of a type alias declaration effected a much larger .d.ts output.

... the reason this happens is because type alias declarations can be inlined, whereas interfaces are always referenced by name.

Comments

1

interface CompositeType extends TypeA, TypeB is more performant for the compiler than type CompositeType = TypeA & TypeB

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.