How to define static property in TypeScript interface

Typescript

Typescript Problem Overview


I just want to declare a static property in typescript interface? I have not found anywhere regarding this.

interface myInterface {
  static Name:string;
}

Is it possible?

Typescript Solutions


Solution 1 - Typescript

Follow @Duncan's @Bartvds's answer, here to provide a workable way after years passed.

At this point after Typescript 1.5 released (@Jun 15 '15), your helpful interface

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

can be implemented this way with the help of decorator.

/* class decorator */
function staticImplements<T>() {
    return <U extends T>(constructor: U) => {constructor};
}

@staticImplements<MyTypeStatic>()   /* this statement implements both normal interface & static interface */
class MyTypeClass { /* implements MyType { */ /* so this become optional not required */
    public static staticMethod() {}
    instanceMethod() {}
}

Refer to my comment at github issue 13462.

visual result: Compile error with a hint of static method missing. enter image description here

After static method implemented, hint for method missing. enter image description here

Compilation passed after both static interface and normal interface fulfilled. enter image description here

Solution 2 - Typescript

You can't define a static property on an interface in TypeScript.

Say you wanted to change the Date object, rather than trying to add to the definitions of Date, you could wrap it, or simply create your rich date class to do the stuff that Date doesn't do.

class RichDate {
    public static MinValue = new Date();
}

Because Date is an interface in TypeScript, you can't extend it with a class using the extends keyword, which is a bit of a shame as this would be a good solution if date was a class.

If you want to extend the Date object to provide a MinValue property on the prototype, you can:

interface Date {
	MinValue: Date;
}

Date.prototype.MinValue = new Date(0);

Called using:

var x = new Date();
console.log(x.MinValue);

And if you want to make it available without an instance, you also can... but it is a bit fussy.

interface DateStatic extends Date {
	MinValue: Date;
}

Date['MinValue'] = new Date(0);

Called using:

var x: DateStatic = <any>Date; // We aren't using an instance
console.log(x.MinValue);

Solution 3 - Typescript

Static modifiers cannot appear on a type member (TypeScript error TS1070). That's why I recommend to use an abstract class to solve the mission:

Example

// Interface definition
abstract class MyInterface {
  static MyName: string;
  abstract getText(): string;
}

// Interface implementation
class MyClass extends MyInterface {
  static MyName = 'TestName';
  getText(): string {
    return `This is my name static name "${MyClass.MyName}".`;
  }
}

// Test run
const test: MyInterface = new MyClass();
console.log(test.getText());

Solution 4 - Typescript

You can define interface normally:

interface MyInterface {
    Name:string;
}

but you can't just do

class MyClass implements MyInterface {
    static Name:string; // typescript won't care about this field
    Name:string;         // and demand this one instead
}

To express that a class should follow this interface for its static properties you need a bit of trickery:

var MyClass: MyInterface;
MyClass = class {
    static Name:string; // if the class doesn't have that field it won't compile
}

You can even keep the name of the class, TypeScript (2.0) won't mind:

var MyClass: MyInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that field it won't compile
}

If you want to inherit from many interfaces statically you'll have to merge them first into a new one:

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
interface NameAndAddressInterface extends NameInterface, AddressInterface { }
var MyClass: NameAndAddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

Or if you don't want to name merged interface you can do:

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
var MyClass: NameInterface & AddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

Working example

Solution 5 - Typescript

Static properties are usually placed on the (global) constructor for the object, whereas the "interface" keyword applies to instances of the object.

The previous answer given is of course correct if you are writing the class in TypeScript. It may help others to know that if you are describing an object that is already implemented elsewhere, then the global constructor including static properties can be declared like this:

declare var myInterface : {
  new(): Interface;
  Name:string;
}

Solution 6 - Typescript

@duncan's solution above specifying new() for the static type works also with interfaces:

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

Solution 7 - Typescript

Another option not mentioned here is defining variable with a type representing static interface and assigning to it class expression:

interface MyType {
    instanceMethod(): void;
}

interface MyTypeStatic {
    new(): MyType;
    staticMethod(): void;
}

// ok
const MyTypeClass: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
    instanceMethod() { }
}

// error: 'instanceMethod' is missing
const MyTypeClass1: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
}

// error: 'staticMethod' is missing
const MyTypeClass2: MyTypeStatic = class MyTypeClass {
    instanceMethod() { }
}

The effect is same as in answer with decorators, but without overhead of decorators

Playground

Relevant suggestion/discussion on GitHub

Solution 8 - Typescript

Solution

Returns an instance type of I and makes sure C extends I:

type StaticImplements<I extends new (...args: any[]) => any, C extends I> = InstanceType<I>;

Interface with instance method:

interface MyInstance {
	instanceMethod();
}

Interface with static method:

interface MyClassStatic {
	new (...args: any[]): MyInstance;
	staticMethod();
}

Class requiring static method and extending with its own method:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {
	static staticMethod();
	static ownStaticMethod();
	instanceMethod();
	ownInstanceMethod();
}

Reasoning

Defining static methods in interfaces is being discussed in #33892, and abstract static methods are being discussed in #34516.

Based on Val's and Aleksey's answers (thank you), this solution:

  • Does not require additional runtime values
  • Class own member information is preserved
  • Allows constructor constraints

Test

As is - Playground Link:

MyClass.staticMethod(); // OK
MyClass.ownStaticMethod(); // OK
new MyClass().instanceMethod(); // OK
new MyClass().ownInstanceMethod(); // OK

If to remove staticMethod from MyClass - Playground Link:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Type 'typeof MyClass' does not satisfy the constraint 'MyClassStatic'. Property 'staticMethod' is missing in type 'typeof MyClass' but required in type 'MyClassStatic'.

If to remove instanceMethod from MyClass - Playground Link:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Class 'MyClass' incorrectly implements interface 'MyInstance'. Property 'instanceMethod' is missing in type 'MyClass' but required in type 'MyInstance'.

Solution 9 - Typescript

I'm somewhat surprised at how overly complicated the top answers are! But maybe that's just because this thread is so old.

Edit: Actually my initial attempt proved to be practically useless after some testing, and this problem turned out to be somewhat harder to tackle than I had originally expected.

After about an hour or so of tinkering however, I think I may have just found the best/cleanest solution so far (building upon my initial idea)! If the question posed is "How do I include static properties in an interface?", then I think this is a fairly decent answer. This is at least better than extending a class if all you need is an interface (compiletime typing/requirements/restraints). There's no real drawback (well, maybe one small one) to this either since the solution is 100% ambient (unlike extends-based class extension like some answers are suggesting) and classes are constants regardless (immutable references that are not hoisted when using the standard class declaration syntax instead of a class expression as I'm doing here). This incurs no runtime overhead and doesn't require runtime class inheritance. You can define the entire class (both static and non-static members) all in one (ambient class used as an) interface!

Here is how it's done!

/** ./interface.ts */
// In a file module (best), or wrap in ts module or namespace block

// Putting this in a module provides encapsulation ensuring that no one is
// at risk of misusing this class (it must be used as a type only). 

// Export only a type reference which will make it error is someone tries 
// to use it as a value (such as in an `extends` clause, or trying to 
// instantiate it).

/** 
 * Other Ideas For Names To Differentiate From Actual Classes/Non-Ambient Values:
 * MyClassInterface or _Interface_MyClass or MyClass_Interface or Interface_MyClass  
 **/
declare class _MyClassInterface {
    static staticProp: string;
    static staticMethod(): number;
    readonly prop: boolean 
    /** 
     * Note: Above, readonly won't need to be specified in the real class 
     * but the prop *will* still be readonly anyway.
     *
     * Now for the only caveat!
     * It does appear however that you cannot mark anything private or 
     * protected in this pseudo-interface which is a bummer, only props
     * and methods that appear only in the real class can be.
     */
    prop2: boolean;
    method(): Function;
    constructor(p1: string, p2: number);
}

export type MyClassInterface = typeof _MyClassInterface;

now to consume the interface

/** ./consumer.ts */
import { MyClassInterface } from "./interface" // type import

const MyClass: MyClassInterface = class {
    static staticProp: string;
    prop: boolean;
    prop2: boolean;
    protected onlyOnRealClass: boolean; /* this is ok since this prop doesn't exist on the interface */

    static staticMethod() {
        return 5;
    }

    method() {
        return () => {};
    }

    constructor(p1: string, p2: number) {}
};

note that the typeof keyword is absolutely essential here (if I recall correctly, it is because without it typescript thinks we are specifying the instance type when what we really want is the type of the class itself). For example when we do

const a: MyClass = new MyClass()

without the typeof keyword, we're saying that a should be an instance of MyClass, not MyClass itself.

abstract ensures you don't accidentally try to instantiate the class...

Edit: Actually I'm removing the abstract keyword from my answer because it turns out that the real class actually inherits the property of being abstract from the ambient class (makes sense), and will therefore not instantiate w/o the compiler complaining if the ambient class that provides its type is marked abstract... just gonna have to deal with ts not erroring if the ambient class is ever accidentally instantiated. It might be a decent idea then to prefix the ambient class declaration/name with an underscore and/or include the word Interface in the name so its proper use is clear (edit: I have since tackled this issue by encapsulating the interface in a file module thereby rendering it private to all other code and then exporting only a type reference to it).

and that is all there is to it really!

Putting the interface into a module isn't totally necessary but it provides a couple small benefits including:

  1. the "publicly" widely-used type-annotation that is used throughout implementation code becomes slightly smaller since it no longer includes the keyword typeof

  2. unlike the wrapped ambient class/interface declaration, the exported/outward-facing identifier is strictly a type (alias), so an error will now occur if anyone tries to instantiate it or use it in an extends clause (or use it anywhere else a runtime value is expected)

I'm not supplying a class name for the Class Expression in this example because Class Expressions, like all Function Expressions, will just inherit the identifier that they are assigned to if a class name if not provided. So if your identifier is identical to the name you want for that class or function anyways, you can just leave it off. Or, you may provide one inline as usual and it will take precedence over the identifier. A class or function name can also be changed after function/class creation, but only via Object.defineProperty or Object.defineProperties.

FWIW classes can actually be implemented (at least in recent versions of TS) by another class, but the static properties will just be ignored anyways. It seems that implementing anything only applies to the prototype in both directions (to/from).

Solution 10 - Typescript

If you're looking to define a static class (ie. all methods/properties are static), you can do something like this:

interface MyStaticClassInterface {
  foo():string;
}

var myStaticClass:MyStaticClassInterface = {
  foo() {
    return 'bar';
  }
};

In this case, the static "class" is really just a plain-ol'-js-object, which implements all the methods of MyStaticClassInterface

Solution 11 - Typescript

You can merge interface with namespace using the same name:

interface myInterface { }

namespace myInterface {
  Name:string;
}

But this interface is only useful to know that its have property Name. You can not implement it.

Solution 12 - Typescript

Yes, it is possible. Here is the solution

export interface Foo {
     
    test(): void;
}

export namespace Foo {

    export function statMethod(): void {
     	console.log(2);
    }

}

Solution 13 - Typescript

I found a way to do this (without decorators) for my specific use case.

The important part that checks for static members is IObjectClass and using cls: IObjectClass<T> in the createObject method:

//------------------------
// Library
//------------------------
interface IObject {
  id: number;
}
interface IObjectClass<T> {
  new(): T;
  table_name: string;
}
function createObject<T extends IObject>(cls: IObjectClass<T>, data:Partial<T>):T {
  let obj:T = (<any>Object).assign({},
    data,
    {
      id: 1,
      table_name: cls.table_name,
    }
  )
  return obj;
}

//------------------------
// Implementation
//------------------------
export class User implements IObject {
  static table_name: string = 'user';
  id: number;
  name: string;
}

//------------------------
// Application
//------------------------
let user = createObject(User, {name: 'Jimmy'});
console.log(user.name);

Solution 14 - Typescript

Here's a fairly simple way to do it:

interface MyClass {
    new (): MyClassInstance;
    staticMethod(): string;
}

interface MyClassInstance {
    instanceMethod(): string;
}

const Class: MyClass = class {
    static staticMethod() {
        return "This is a static method";
    }
    instanceMethod() {
        return "This is an instance method";
    }
}

Class.staticMethod();

// Has type MyClassInstance
const instance = new Class();
instance.instanceMethod();

Note that this doesn't allow you to make a class extend an interface like you could normally, but for many situations this is good enough.

Solution 15 - Typescript

Though static keyword not supported in interface in Typescript but we can achieve it by creating a function interface that has static member.

In my following code I have created a function interface Factory that has two static members serialNumber and printSerial.

// factory is a function interface
interface Factory<T> {
    (name: string, age: number): T;

    //staic property
    serialNumber: number;

    //static method
    printSrial: () => void;
}

class Dog {
    constructor(public name: string, public age: number) { }
}

const dogFactory: Factory<Dog> = (name, age) => {
    return new Dog(name, age);
}

// initialising static members

dogFactory.serialNumber = 1234;
dogFactory.printSrial = () => console.log(dogFactory.serialNumber);


//instance of Dog that DogFactory creates
const myDog = dogFactory("spike", 3);

//static property that returns 1234
console.log(dogFactory.serialNumber)

//static method that prints the serial 1234
dogFactory.printSrial();

Solution 16 - Typescript

I implemented a solution like Kamil Szot's, and it has an undesired effect. I have not enough reputation to post this as a comment, so I post it here in case someone is trying that solution and reads this.

The solution is:

interface MyInterface {
    Name: string;
}

const MyClass = class {
    static Name: string;
};

However, using a class expression doesn't allow me to use MyClass as a type. If I write something like:

const myInstance: MyClass;

myInstance turns out to be of type any, and my editor shows the following error:

'MyClass' refers to a value, but is being used as a type here. Did you mean 'typeof MyClass'?ts(2749)

I end up losing a more important typing than the one I wanted to achieve with the interface for the static part of the class.

Val's solution using a decorator avoids this pitfall.

Solution 17 - Typescript

The other solutions seem to stray from the blessed path and I found that my scenario was covered in the Typescript documentation which I've paraphrased below:

interface AppPackageCheck<T> {
  new (packageExists: boolean): T
  checkIfPackageExists(): boolean;
}

class WebApp {
    public static checkIfPackageExists(): boolean {
        return false;
    }

    constructor(public packageExists: boolean) {}
}

class BackendApp {
    constructor(public packageExists: boolean) {}
}

function createApp<T>(type: AppPackageCheck<T>): T {
    const packageExists = type.checkIfPackageExists();
    return new type(packageExists)
}

let web = createApp(WebApp);

// compiler failure here, missing checkIfPackageExists
let backend = createApp(BackendApp); 

Solution 18 - Typescript

Simple example

interface Person {
  name: string;
  age: number;
}

abstract class Trackable {
  static TrackInstances: number;
}

class Pablo extends Trackable implements Person {
  constructor(public name: string, public age: number) { Pablo.TrackInstances+=1; }
}
console.log(Pablo.TrackInstances);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRajagopal 웃View Question on Stackoverflow
Solution 1 - TypescriptValView Answer on Stackoverflow
Solution 2 - TypescriptFentonView Answer on Stackoverflow
Solution 3 - TypescriptBenny NeugebauerView Answer on Stackoverflow
Solution 4 - TypescriptKamil SzotView Answer on Stackoverflow
Solution 5 - TypescriptduncanView Answer on Stackoverflow
Solution 6 - TypescriptBartvdsView Answer on Stackoverflow
Solution 7 - TypescriptAleksey L.View Answer on Stackoverflow
Solution 8 - TypescriptwinterhotlatteView Answer on Stackoverflow
Solution 9 - Typescripttyler1205View Answer on Stackoverflow
Solution 10 - TypescriptedanView Answer on Stackoverflow
Solution 11 - TypescriptktretyakView Answer on Stackoverflow
Solution 12 - TypescriptPavel_KView Answer on Stackoverflow
Solution 13 - TypescriptiffyView Answer on Stackoverflow
Solution 14 - TypescriptMerlin04View Answer on Stackoverflow
Solution 15 - TypescriptmanasView Answer on Stackoverflow
Solution 16 - TypescriptDario ScattoliniView Answer on Stackoverflow
Solution 17 - TypescriptRichard NienaberView Answer on Stackoverflow
Solution 18 - TypescriptCleitonView Answer on Stackoverflow