ChatGPT解决这个技术问题 Extra ChatGPT

What's the difference between "declare class" and "interface" in TypeScript

In TypeScript, when creating .d.ts source declaration files, which is preferable and why?

declare class Example {
    public Method(): void; 
}

or

interface Example {
    Method(): void;
}

The differences that I can tell are that interfaces can't have static methods, so you must use a class for that. Both don't product any JS output, so perhaps it doesn't matter?

I don't feel that either of these really help describe you would use one over another since they both can accomplish the same thing with no JS output.
Short: With declare you have to make sure that the implementation of the class exists at runtime where with an interface you don't have to.

R
Ryan Cavanaugh

interface is for when you simply want to describe the shape of an object. There's no code generation, ever, for interfaces -- they're solely an artifact in the type system. You'll see no difference in the code generation for a class depending on whether or not it has an implements clause.

declare class is for when you want to describe an existing class (usually a TypeScript class, but not always) that is going to be externally present (for example, you have two .ts files that compile to two .js files and both are included via script tags in a webpage). If you inherit from a class using extends (regardless of whether the base type was a declare class or a regular class) the compiler is going to generate all the code to hook up the prototype chain and forwarding constructors and what not.

If you try to inherit from a declare class that should have been an interface, you are going to have a runtime error because that generated code will be referring to an object with no runtime manifestation.

Conversely, if you simply implement an interface that should have been a declare class, you're going to have to re-implement all the members yourself and won't be taking advantage of any code re-use from the would-be base class, and functions that check the prototype chain at runtime will reject your object as not actually being an instance of the base class.

To get really nerdy, if you have a C++ background, you can roughly think of interface as typedef and declare class as an extern declaration of a constructor that strictly lacks a definition in this compile unit.

From a pure consumption side (writing imperative code, not adding new types), the only difference between interface and declare class is that you can't new an interface. However, if you intend to extend/implement one of these types in a new class, you absolutely have to have chosen correctly between interface and declare class. Only one of them will work.

Two rules that will serve you well:

Is the name of the type aligning with a constructor function (something invokable with new) that's actually present at runtime (e.g. Date is, but JQueryStatic is not)? If no, you definitely want interface

Am I dealing with a compiled class from another TypeScript file, or something sufficiently similar? If yes, use declare class


You can new an interface in typescript actually. The only limitation is inheritance.
You cannot invoke the new operator on an interface type. However, interfaces can have construct signatures, which means you could invoke the new operator on a value of the interface type. This is very different than how class works, where the construct signature is on the type name itself rather than on an expression of that type.
If you go the route of adding a constructor to an interface, it should be the ONLY member of the interface, except for 'statics' of the class. Do not combine the interface of the constructor function with the interface of the object constructed. If you do, the type system allows silliness like: new (new x()), where x: Interface.
F
Fenton

You can implement the interface:

class MyClass implements Example {
    Method() {

    }
}

Whereas the declare class syntax is really intended to be used to add type definitions for external code that isn't written in TypeScript - so the implementation is "elsewhere".


So you are suggesting that declare class should be used to describe code not written in TypeScript? I would assume that the case, but in the jquery.d.ts file checked in, JQueryStatic is an interface implemented by: declare var $ : JQueryStatic I would have though this to be declare class $ { public static ... }
The only reason I can think of for this would be if you didn't want people to extend the class - using the interface means you would have to supply the whole implementation.
Makes sense. Perhaps that was the reason.
OK then, so I am trying point to declarations in another JS library so I definitely require declare. The code is using statics on some of the library functions (classes) - so far I am yet to see a static property or method expressed in a declaration file. Oh, and should I use namespace or module?
n
nikk wong

In layman's terms, declare is used in .ts/d.ts files to tell the compiler that we should expect the keyword we're declaring to exist in that environment, even if it is not defined in the present file. This will then allow us to have type safety when using the declared object, as the Typescript compiler now knows that some other component may provide that variable.


If it is not defined in the present file, how could we find where the impl code is?
W
Willem van der Veen

Difference between declare and interface in TS:

declare:

declare class Example {
    public Method(): void; 
}

In the above code declare lets the TS compiler know that somewhere the class Example is declared. This does not mean that the class is magically included. You as a programmer are responsible for having the class available when you are declaring it (with the declare keyword).

interface:

interface Example {
    Method(): void;
}

An interface is a virtual construct that only exists within typescript. The typescript compiler uses it for the sole purpose of type checking. When the code is compiled to javascript this whole construct will be stripped out. The typescript compiler uses interfaces in order to check if objects have the right structure.

For example when we have the following interface:

interface test {
  foo: number,
  bar: string,
}

The objects which we define which have this interface type need to match the interface exactly:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }