5

I have a base interface IInput and extended from it 3 interfaces IType1Input, IType2Input and IType3Input. After a few business changes to Type2(), IType2Input is now the same as IInput, but for consistency I don't want only Type2() to use IInput instead of an extended interface. I tried to do this:

    interface IType2Input extends IInput {}

ESLint then warned me about no-empty-interface. If I don't want to disable this rule, is there a way to declare IType2Input as the same interface as IInput?

formicini
  • 298
  • 4
  • 16
  • 1
    "If I don't want to disable this rule"... can't you just [disable the rule for one line](//stackoverflow.com/a/29592334/2887218)? Since you *intend* to create a new interface identical to an existing one, you really do want to break the rule, right? Instead of circumventing the rule in some way, why not just [proudly admit](//tsplay.dev/w14nGW) that the rule doesn't help you in this situation, and that you want an empty extension to `IInput`, and that you have a good reason to want this. You could try something like [this](//tsplay.dev/WK7PZW) to circumvent the rule, but why? – jcalz Nov 26 '21 at 02:57
  • I'm happy to write this up as an answer if it meets your needs. If not, could you [edit] your question to explain the motivation more? – jcalz Nov 26 '21 at 03:08
  • @jcalz I was just wondering if it's actually possible to declare 2 names for an interface, or 2 interfaces with the same body, in a somewhat nice manner; something like multiple `case`s for 1 body in a `switch`. If it's impossible I'll disable the ESLint rule then. The reason why I put the "If I don't want to disable this rule" there is to prevent people from suggesting answers along that line, which I already know is an option. – formicini Nov 26 '21 at 04:18
  • I just want to make sure we're on the same page: you're aware you can disable the rule just for the one line and not completely, right? – jcalz Nov 26 '21 at 04:23
  • @jcalz Yes, VS Code did suggest that. Very useful when working with 3rd party libraries. – formicini Nov 26 '21 at 04:25
  • So does `interface IType2Input extends IInput, IInput {}` generate a warning? Or the `type IType2Input = IInput` as @RobertMengual's answer says below? And are either of those preferable to disabling the rule for one line? – jcalz Nov 26 '21 at 04:27
  • Both are OK with ESLint. The double extends I considered a bit hacky, while the type way convert the interface into a different object altogether, but they got the job done practically. Not exactly "with the same body" tho, and I don't like their syntaxes so I'll just disable ESLint for this line. That said, if you post the double extend syntax as an answer then I'll accept it. – formicini Nov 26 '21 at 04:34
  • 1
    I need to go to bed but I will likely write an answer tomorrow. – jcalz Nov 26 '21 at 04:52

2 Answers2

5

Try using a type instead of an interface until that interface is different. Note that under the hood interfaces and types are the same.

type IType2Input = IInput;
Robert Mengual
  • 374
  • 3
  • 10
  • "under the hood interfaces and types are the same" <-- what do you mean by this, exactly? Sometimes [interfaces and type aliases to object types are observably not the same](https://github.com/microsoft/TypeScript/issues/15300). – jcalz Nov 26 '21 at 03:03
  • What I mean is that when they get transpiled to javascript they are basically the same. It's true there are a few differences in the way they are defined and used (as explained here https://stackoverflow.com/questions/37233735/interfaces-vs-types-in-typescript), but under the hood they are almost the same. – Robert Mengual Nov 26 '21 at 23:02
  • "when they get transpiled to javascript they are basically the same" The static type system is [erased](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#erased-types) completely from the emitted JavaScript... so yes, I guess, they are "the same" in that respect, but then you'd also say `interface Foo {a: string}` and `interface Bar {a: number}` are "the same" that way, and that can't be what you mean, right? – jcalz Nov 27 '21 at 02:24
  • That's right :) – Robert Mengual Nov 27 '21 at 09:27
  • I don't understand, possibly due to the negative in my question. Are you saying that `interface Foo {a: string}` and `interface Bar {a: number}` are "the same under the hood"? – jcalz Nov 27 '21 at 19:34
1

I know you're not looking for this suggestion, so feel free to skip the following section. This is mostly for future readers who come upon this question/answer pair.


I'd say that the no-empty-interface ESLint rule is not appropriate for your use case, and you really should disable the rule for the line in question without necessarily disabling it for your whole code base.

The documentation for the rule says "an empty interface is equivalent to its supertype ... [and therefore it] can be omitted." When you write

interface IType2Input extends IInput {} // linter warning!

and get a linter warning, the rule wants you to replace all references to IType2Input with IInput because IType2Input is redundant. But you don't want to do this. If you find a way to rewrite your code so that two equivalent interfaces named IType2Input and IInput exist without triggering the linter warning, you'd still be violating the spirit of the rule if not the letter.

Instead of trying to figure out how to circumvent the rule with syntax that might possibly be more complicated than an empty interface, you could just disable the rule for the one declaration, and comment it to let people know what you're doing and why:

// IType2Input is intentionally empty. It happens not
// to have more properties than IInput, but this may
// change in the future, and it helps to have a Type2-specific
// interface that people can use.  Therefore the no-empty-interface
// rule is not appropriate for this declaration:

// eslint-disable-next-line no-empty-interface
interface IType2Input extends IInput { }

From here on out I'll assume that you don't want to do this.


One way to circumvent the rule is to use the syntax for extending multiple interfaces, but the "multiple" interfaces are both just IInput:

interface IType2Input extends IInput, IInput { } // no error

This manages not to trigger the linter rule. But, as you said, it's basically a hack. And anyone who sees this code later would probably refactor it to just extends IInput, which would bring you back to the original problem.


Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360