Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

interface-over-type-literal shouldn't be recommended #3248

Closed
navels opened this issue Sep 25, 2017 · 18 comments · Fixed by #4871
Closed

interface-over-type-literal shouldn't be recommended #3248

navels opened this issue Sep 25, 2017 · 18 comments · Fixed by #4871

Comments

@navels
Copy link

navels commented Sep 25, 2017

Yes, I know the Typescript docs say

Because an ideal property of software is being open to extension, you should always use an interface over a type alias if possible.

but seriously, if I am making a simple type with zero intention of ever using it as an extendable thing, it makes no sense to code it as an interface.

@ajafff
Copy link
Contributor

ajafff commented Sep 26, 2017

Sorry, I don't get what you mean. Is this a feature request or a bug report?

@navels
Copy link
Author

navels commented Sep 26, 2017

The default value for interface-over-type-literal in recommended.ts is true. I believe it should be false. Not sure if that constitutes a feature request or a bug :-)

@ajafff
Copy link
Contributor

ajafff commented Sep 26, 2017

After all the recommended preset is opinionated. It incorporates best practices and suggestions like the one you posted above.
When extending a preset, you can override the recommendation and tune the configuration to your needs.

Anyways, disabling the rule would be a breaking change. So don't expect any change before the next major version.


To clarify: interface-over-type-literal only complains about type alias declarations.

type Foo = {foo: number}; // The rule disallows this
interface Foo {foo: number} // and fixes it to this

let obj: {foo: number}; // This is still allowed

Since a type alias and an interface can be used (almost) interchangeably, I don't see why you would prefer the type alias.

On a related note: interfaces used to be cached while anonymous types like type aliases were recomputed for ever usage. So using type aliases could significantly increase compile time.
That performance gap will be almost gone with the next typescript version.

@navels
Copy link
Author

navels commented Sep 26, 2017

I do know that I can override it, but I feel pretty strongly that the recommendation is incorrect.

Here's my view of it. There are many cases where an interface cannot be used for a type alias. You can only use an interface to type an object literal. If I am to follow the recommended best practices, then in a particular class file where I may have several type aliases, some will be type aliases and some will be interfaces. Someone stumbling upon this class will wonder what the heck is going on and if I am intending the interfaces to be implemented/extended somewhere, or to at least be expecting it to be a possibility.

It would be much cleaner in my opinion for me to use a type alias for a type alias and an interface for an interface, rather than substitute one for the other in some cases just because interfaces have some extra capability (that I don't need if all want is a type alias).

I suppose I should write up an issue against the typescript docs, which I will do, but I think some engineering judgement can be applied here rather than implement a rule just because the docs say you should.

@stefaneg
Copy link

The use of interface keyword for structures should really be discouraged in my opinion. This rule does exactly the opposite, so there is no way in hell this can be considered a best practice.

This is one of the things typescript got wrong in my opinion, this is confusing the concept of interface, whose semantics in all other typesafe languages I've used, means "a collection of methods/functions/callable things", with object signature/duck type. The use of interface should be more restrictive, and I would really welcome a TSLint rule "interface-must-declare-only-functions", or a less restrictive "interface-must-declare-at-least-one-function"

Pure structures should be declared using the type operator and extended using the & operator. And the name should start with "T" :-)

@adidahiya
Copy link
Contributor

@navels I agree with your feedback, I think tslint:recommended should remove this overly-opinionated rule configuration as a breaking change in the next major version

@sibelius
Copy link

how can I override this option?

@JoshuaKGoldberg
Copy link
Contributor

@sibelius in your tslint.json, under "rules", mark it as false:

{
    "extends": ["tslint:recommended"],
    "rules": {
        "interface-over-type-literal": false
    }
}

Note that this issue is tracking disabling the rule in the recommended ruleset. If you have general questions on TSLint, please use either StackOverflow or Gitter.

@nathanredblur
Copy link

nathanredblur commented Jan 24, 2019

Here someone talk about this subject https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c

About overwrite the rule, I would prefer the option to force me use type instead of interface or even better, force me to use type only if I'm defining a Props or State in my react component.

@dandrei
Copy link

dandrei commented Feb 17, 2019

I've been writing less and less OOP code to the point that I'm not using it at all in my current project.
I wouldn't want beginners to be nudged towards it.

@anurbol
Copy link

anurbol commented Feb 18, 2019

@dandrei and I use Interfaces only, when I want to describe functions (I use no OOP too).

@adidahiya adidahiya added this to the TSLint 6.0 milestone Feb 18, 2019
@JoshMcCullough
Copy link

Yeah, this one's definitely wrong and IMO should not be part of the recommended, or at least not auto-fixed so we can disable it before it clobbers our types.

@lukescott
Copy link

The recommendation actually breaks code. Consider:

type Foo = {
  foo: string
}

interface IndexedObject {
  [key: string]: string
}

function useIndexedObject(object: IndexedObject) {}

const foo: Foo = {foo: "foo"}
useIndexedObject(foo)

The above code works. Until tslint --fix is applied and changes type Foo to interface Foo, the last line produces the error:

Argument of type 'Foo' is not assignable to parameter of type 'IndexedObject'.
  Index signature is missing in type 'Foo'.

IMO, any rule that breaks code should not be a recommendation. Heck, it shouldn't even be a rule if it has the potential to break code.

@JoshMcCullough
Copy link

Not only that, but it also causes the "interfaces must start with 'I'" rule.

@Gaubee
Copy link

Gaubee commented Jul 24, 2019

error TS2344: ...
Index Signature is missing in type 'SOME INTERFACE'.

so I have to use type.

@miraage
Copy link

miraage commented Aug 29, 2019

If your apps have a lot of type usage, it is a good indicator of violations of SOLID principles.

@JoshuaKGoldberg
Copy link
Contributor

Removing the Type: Breaking Change label per #4811. Now accepting PRs!

@dennis-f
Copy link

dennis-f commented Feb 9, 2021

So why is it called TypeScript, when it should be named InterfaceScript?! 🤣

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.