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?
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.
https://i.stack.imgur.com/xV9F7.png
https://i.stack.imgur.com/jummd.png
https://i.stack.imgur.com/awoDo.png
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);
extends
keyword. You just can't extend an interface with a class (which you would need to do in order to add a static property).
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());
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
@staticImplements
.
reply(msg: IBotMsg): Promise<IBotReply>
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;
}
@duncan's solution above specifying new()
for the static type works also with interfaces:
interface MyType {
instanceMethod();
}
interface MyTypeStatic {
new():MyType;
staticMethod();
}
implement
?
MyType
(as in class Foo implements MyType
). The static interface is only real useful in definitions, when describing existing JS code.
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
Relevant suggestion/discussion on GitHub
implements static X, static Y
is a good suggestion. static
as a keyword before every interface should not be breaking anything though a bit ugly.
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'.
StaticImplements
should be InstanceType<C>
(this also fixes an error: "'C' is declared but its value is never read.")
C extends I
, so return type might as well be object
or {}
. It just feels more appropriate for class to actually implement the target instance type, rather than itself or empty object. To my understanding, unless noUnusedParameters is set, it should emit only a warning. In my case, it's ESLint that highlights it, disabling it for that line shows the TS warning only on hover, otherwise it's greyed out.
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:
the "publicly" widely-used type-annotation that is used throughout implementation code becomes slightly smaller since it no longer includes the keyword typeof 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).
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
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.
Yes, it is possible. Here is the solution
export interface Foo {
test(): void;
}
export namespace Foo {
export function statMethod(): void {
console.log(2);
}
}
Foo
to stand in for a class definition? where Class Foo would implement the statMethod() this seems a bit of a hack, esp since namespaces are kind of deprecated now...
waiting more feedback
. So you either wait until static methods are added to interface or do this way.
export namespace
is actually your Class implementation? That does seem to be bending things a lot. I wonder what else that breaks in Classes?
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);
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.
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();
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.
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);
My solution has worked great for my use case of adding additional static constructors. I have tested it and it has passed all tests. If anyone finds a buggy edge case please let me know.
I made a generic type which takes in the interface and static interface.
It works for concrete and abstract classes.
I have designed it using conditional types such that all errors get propagated to the class implementing the Interface rather than the interface itself.
Note: The error propagation allows for vscode quick fixes (implement all methods). The only drawback is you will have to apply the static keyword yourself because there is no quick fix available for that error.
The Interface:
type Class<T = any> = new (...args: any[]) => T;
type AbstractClass<T = any> = abstract new (...args: any[]) => T;
type Interface<C extends Class<InstanceType<C>> | AbstractClass<InstanceType<C>>, SI, I = {}> =
C extends Class<InstanceType<C>>
// ConcreteClass
? InstanceType<C> extends I
? C extends (SI & Class<InstanceType<C>>)
? (InstanceType<C> & I)
: (SI & Class<InstanceType<C>>) // Indicate StaticInterface Error
: I // Indicate Interface Error
// AbstractClass
: InstanceType<C> extends I
? C extends (SI & AbstractClass<InstanceType<C>>)
? (InstanceType<C> & I)
: (SI & AbstractClass<InstanceType<C>>) // Indicate StaticInterface Error
: I // Indicate Interface Error
Usage:
interface MyInterface {
instanceMethod(): number;
}
interface MyStaticInterface {
staticMethod(): number;
}
class MyClass implements Interface<typeof MyClass, MyStaticInterface, MyInterface> {
static staticMethod(): number {
return 50;
}
instanceMethod(): number {
return 100;
}
static otherStatic() {
return "HELLO"
}
otherInstance() {
return "GOODBYE"
}
}
abstract class MyClass1 implements Interface<typeof MyClass1, MyStaticInterface, MyInterface> {
static staticMethod(): number {
return 50;
}
instanceMethod(): number {
return 20;
}
static otherStatic() {
return "HELLO"
}
otherInstance() {
return "GOODBYE"
}
abstract abstractMethod() : number;
}
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);
Success story sharing
'constructor' is declared but its value is never read.ts(6133)
return (constructor: T) => {}