Cách xác định thuộc tính tĩnh trong giao diện TypeScript


122

Tôi chỉ muốn khai báo một thuộc tính tĩnh trong giao diện typecript ? Tôi đã không tìm thấy bất cứ nơi nào liên quan đến điều này.

interface myInterface {
  static Name:string;
}

Có khả thi không?


Chính xác thì bạn đang cố gắng mô hình hóa cái gì?
Ryan Cavanaugh

5
Cân nhắc sử dụng 'class Foo triển khai Bar' trong đó Bar cũng là một lớp. Bạn có thể 'thực hiện' các lớp bởi các lớp khác trong TS.
Tobias Cudnik

Câu trả lời:


46

Bạn không thể xác định thuộc tính tĩnh trên giao diện trong TypeScript.

Giả sử bạn muốn thay đổi Dateđối tượng, thay vì cố gắng thêm vào các định nghĩa của Date, bạn có thể gói nó lại hoặc đơn giản là tạo lớp ngày tháng phong phú của bạn để thực hiện những việc Datekhông làm.

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

Vì Date là một giao diện trong TypeScript nên bạn không thể mở rộng nó với một lớp bằng extendstừ khóa, điều này hơi đáng tiếc vì đây sẽ là một giải pháp tốt nếu ngày là một lớp.

Nếu bạn muốn mở rộng đối tượng Ngày để cung cấp một thuộc MinValuetính trên nguyên mẫu, bạn có thể:

interface Date {
    MinValue: Date;
}

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

Được gọi bằng:

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

Và nếu bạn muốn làm cho nó có sẵn mà không có phiên bản, bạn cũng có thể ... nhưng nó hơi cầu kỳ.

interface DateStatic extends Date {
    MinValue: Date;
}

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

Được gọi bằng:

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

1
@Rajagopal Để rõ ràng, bạn thực sự có thể mở rộng giao diện trong TS bằng extendstừ khóa. Bạn chỉ không thể mở rộng giao diện với một lớp (mà bạn cần phải làm để thêm thuộc tính tĩnh).
Jude Fisher

Steve - 'Vì Date là một giao diện trong TypeScript, bạn không thể mở rộng nó bằng cách sử dụng từ khóa expand' - không đúng, phải không?
Jude Fisher

1
Bạn có thể mở rộng giao diện, nhưng bạn không thể mở rộng nó với một lớp, chỉ triển khai nó. Tôi đã không nói rõ điều đó.
Fenton

Tôi xin ý kiến ​​@Nikos thứ hai. Không thừa kế các giao diện có đánh bại mục đích tăng cường khả năng đọc, do đó làm giảm khả năng bảo trì? Có ai có thể đưa ra lý do tại sao sử dụng kế thừa các giao diện thay vì một giao diện mới không?
Tomas Hesse

@TomasHesse không có câu trả lời chính xác nào cho câu hỏi (đầu tiên) đó. Những quyết định này là sự đánh đổi giữa các nhu cầu cạnh tranh và câu trả lời chính xác là đánh giá sự đánh đổi trong bối cảnh thích hợp.
Fenton

99

Theo dõi câu trả lời của @ Bartvds @ Duncan, tại đây để đưa ra một cách khả thi sau nhiều năm trôi qua.

Tại thời điểm này sau khi Typecript 1.5 được phát hành (@Jun 15 '15), giao diện hữu ích của bạn

interface MyType {
    instanceMethod();
}

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

có thể được thực hiện theo cách này với sự trợ giúp của người trang trí.

/* 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() {}
}

Tham khảo nhận xét của tôi tại github mở số 13462 .

kết quả trực quan: Lỗi biên dịch với một gợi ý về phương thức tĩnh bị thiếu. nhập mô tả hình ảnh ở đây

Sau khi phương thức tĩnh được triển khai, gợi ý cho phương thức bị thiếu. nhập mô tả hình ảnh ở đây

Quá trình biên dịch được thông qua sau khi cả giao diện tĩnh và giao diện bình thường hoàn thành. nhập mô tả hình ảnh ở đây


5
Câu trả lời của bạn là câu trả lời. Cảm ơn bạn rất nhiều. Làm việc với Typecript 2.4.1
Juan Camilo Guarin P

Câu trả lời này không làm việc cho tôi trong nguyên cảo 3.0.1
mmmeff

2
@mmmeff Tôi đã thử nghiệm với 3.0.1 và nó hoạt động. Ý bạn là "không hoạt động" là gì? Có thông báo lỗi nào không?
Val

1
@Chklang Cảm ơn bạn! Tôi cập nhật câu trả lời của tôi với đề nghị của bạn
Val

1
Làm thế nào chúng ta có thể làm điều này với một lớp Trừu tượng?
geoyws

16

Bạn có thể xác định giao diện bình thường:

interface MyInterface {
    Name:string;
}

nhưng bạn không thể chỉ làm

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

Để thể hiện rằng một lớp phải tuân theo giao diện này cho các thuộc tính tĩnh của nó, bạn cần một chút thủ thuật:

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

Bạn thậm chí có thể giữ tên của lớp, TypeScript (2.0) sẽ không bận tâm:

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

Nếu bạn muốn kế thừa từ nhiều giao diện theo cách tĩnh, trước tiên bạn sẽ phải hợp nhất chúng thành một giao diện mới:

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
}

Hoặc nếu bạn không muốn đặt tên giao diện hợp nhất, bạn có thể làm:

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
}

Ví dụ làm việc


Tôi nhận được TS2322 lỗi: lớp Anonymous là không thể chuyển nhượng để gõ
JoshuaDavid

@FireCoding Bạn đang sử dụng phiên bản TypeScript nào? Ví dụ tôi làm việc với 2,0
Kamil Szot

Đây là một giải pháp đơn giản hơn trình trang trí tùy chỉnh @staticImplements.
Robert Penner

1
Tôi nghĩ rằng đây là một giải pháp tuyệt vời. cho các phương pháp async tĩnh bạn cũng có thể sử dụngreply(msg: IBotMsg): Promise<IBotReply>
dcsan

có cách nào để xác định cả phương thức static và instance hay tất cả các phương thức bây giờ chỉ tĩnh?
dcsan

15

Thuộc tính static thường được đặt trên phương thức khởi tạo (toàn cục) cho đối tượng, trong khi từ khóa "interface" áp dụng cho các phiên bản của đối tượng.

Câu trả lời trước đó tất nhiên là đúng nếu bạn đang viết lớp trong TypeScript. Nó có thể giúp người khác biết rằng nếu bạn đang mô tả một đối tượng đã được triển khai ở nơi khác, thì hàm tạo toàn cục bao gồm các thuộc tính tĩnh có thể được khai báo như sau:

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

chỉnh sửa: được đăng dưới dạng câu trả lời đầy đủ bên dưới
Bartvds

Tại thời điểm này sau khi Typescript 1.5 được phát hành (6.15.2015), đây là một cách khả thi để mô tả các thành viên tĩnh của một lớp. stackoverflow.com/a/43674389/681830
Val

8

Các công cụ sửa đổi tĩnh không thể xuất hiện trên một thành viên kiểu (lỗi TypeScript TS1070). Đó là lý do tại sao tôi khuyên bạn nên sử dụng một lớp trừu tượng để giải quyết nhiệm vụ:

Thí dụ

// 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());

Đúng, đó là câu trả lời!
vintproykt

7

Giải pháp của @ duncan ở trên chỉ định new()cho kiểu tĩnh cũng hoạt động với các giao diện:

interface MyType {
    instanceMethod();
}

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

Cái nào trong số này sẽ là lớp của tôi implement?
Denis Pshenov

2
Tại thời điểm này, bạn không thể sử dụng giao diện để mô tả các thành viên tĩnh, chỉ các thành viên thể hiện. Vì vậy, trong ví dụ này, lớp của bạn sẽ triển khai MyType(như trong class Foo implements MyType). Giao diện tĩnh chỉ thực sự hữu ích trong các định nghĩa, khi mô tả mã JS hiện có.
Bartvds

Tại thời điểm này sau khi Typescript 1.5 được phát hành (6.15.2015), đây là một cách khả thi để mô tả các thành viên tĩnh của một lớp. stackoverflow.com/a/43674389/681830
Val

4

Nếu bạn đang tìm cách xác định một lớp tĩnh (tức là tất cả các phương thức / thuộc tính đều là tĩnh), bạn có thể làm như sau:

interface MyStaticClassInterface {
  foo():string;
}

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

Trong trường hợp này, "lớp" tĩnh thực sự chỉ là một đối tượng thuần túy-ol'-js, thực thi tất cả các phương thức của MyStaticClassInterface


4

Có, nó là có thể. Đây là giải pháp

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

Điều này thật tuyệt! Chính xác những gì tôi cần
Umar Farooq Khawaja

1
@UmarFarooqKhawaja Tôi rất vui được trợ giúp!
Pavel_K

vì vậy đây là sử dụng một không gian tên Foođể thay thế cho một định nghĩa lớp? trong đó Class Foo sẽ triển khai statMethod (), điều này có vẻ hơi bị hack, đặc biệt là vì các không gian tên hiện không được dùng nữa ...
dcsan

@dcsan Vâng, nó là một chút hack. Nhưng phải làm thế nào? Rất nhiều vấn đề về bản đánh máy được mở trong nhiều năm với nhãn waiting more feedback. Vì vậy, bạn có thể đợi cho đến khi các phương thức tĩnh được thêm vào giao diện hoặc làm theo cách này.
Pavel_K

nhưng tôi cũng không hiểu lắm .. export namespacethực sự là việc triển khai Lớp học của bạn? Điều đó dường như đang bẻ cong mọi thứ rất nhiều. Tôi tự hỏi điều gì khác mà phá vỡ trong Lớp học?
dcsan

3

Một tùy chọn khác không được đề cập ở đây là xác định biến có kiểu đại diện cho giao diện tĩnh và gán cho nó biểu thức lớp:

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() { }
}

Hiệu ứng tương tự như trong câu trả lời với bộ trang trí , nhưng không có chi phí của bộ trang trí

Sân chơi

Liên quan, thích hợp Đề xuất / thảo luận trên GitHub


2

Bạn có thể hợp nhất giao diện với không gian tên bằng cách sử dụng cùng một tên:

interface myInterface { }

namespace myInterface {
  Name:string;
}

Nhưng giao diện này chỉ hữu ích khi biết rằng nó có thuộc tính Name. Bạn không thể thực hiện nó.


2

Tôi đã tìm thấy một cách để làm điều này (không có trình trang trí) cho trường hợp sử dụng cụ thể của tôi.

Phần quan trọng để kiểm tra các thành viên tĩnh là IObjectClassvà sử dụng cls: IObjectClass<T>trong createObjectphương thức:

//------------------------
// 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);

0

Mặc dù từ khóa tĩnh không được hỗ trợ trong giao diện trong Typecript nhưng chúng ta có thể đạt được nó bằng cách tạo một từ khóa function interfacecó thành viên tĩnh.

Trong đoạn mã sau, tôi đã tạo một giao diện hàm Factorycó hai thành viên tĩnh serialNumberprintSerial .

// 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();

0

Tôi đã triển khai một giải pháp như của Kamil Szot , và nó có hiệu quả không mong muốn. Tôi không đủ uy tín để đăng bài này dưới dạng nhận xét, vì vậy tôi đăng nó ở đây trong trường hợp ai đó đang thử giải pháp đó và đọc được điều này.

Giải pháp là:

interface MyInterface {
    Name: string;
}

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

Tuy nhiên, việc sử dụng một biểu thức lớp không cho phép tôi sử dụng MyClassnhư một kiểu. Nếu tôi viết một cái gì đó như:

const myInstance: MyClass;

myInstanceHóa ra là loại anyvà trình chỉnh sửa của tôi hiển thị lỗi sau:

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

Cuối cùng, tôi đã đánh mất một lần nhập quan trọng hơn so với cách tôi muốn đạt được với giao diện cho phần tĩnh của lớp.

Giải pháp sử dụng thợ trang trí của Val tránh được cạm bẫy này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.