Как объявить интерфейс со статическим методом и сигнатурой конструктора в TypeScript? - PullRequest
1 голос
/ 22 апреля 2019

У меня есть два класса с общим статическим методом.Какой лучший способ добавить общий интерфейс, который включает в себя реализацию статического метода и сигнатуру конструктора?

Я написал BitcoinHDWallet и EthereumHDWallet классы, которые реализуют логику кошелька для соответствующих блокчейнов.Я хотел бы добавить общий интерфейс HDWallet, который будет описывать логику для работы с ними обоими, чтобы пользователь мог сделать:

    const multiWallet: Array<HDWallet> = [];
    multiWallet[0] = new BitcoinHDWallet(...);
    multiWallet[1] = new EthereumHDWallet(...);

Эти два класса имеют некоторые общие статические методы.

0) Если я использую abstract class как способ описания интерфейса, я не знаю, как добавить сигнатуру конструктора.

1) Если я использую interface, я не могу добавить реализацию статического метода.

В идеале я хочу сделать что-то подобное:

abstract class HDWallet {
  /**
   * Return new random mnemonic seed phrase
   */
  static generateMnemonic(): string {
    return Bip39.generateMnemonic();
  }

  constructor(bip39SeedPhrase: string, password?: string, testnet?: boolean);

  abstract getAddress(addressIndex?: number) : string;

  abstract async getBalance(address? : string) : Promise<number>; 

  ...
}


class BitcoinHDWallet extends HDWallet {...}
class EthereumHDWallet extends HDWallet {...}

1 Ответ

1 голос
/ 22 апреля 2019

Там нет, чтобы сделать это из коробки. Абстрактный класс - это самое близкое к тому, что вы хотите, но он действительно не позволит вам проверить, имеет ли тип конкретный конструктор.

Один из способов сделать это - добавить в класс дополнительный параметр типа. Это может быть ограничено typeof HDWallet, который будет представлять подпись конструктора базового класса. Производные классы должны передать себя в качестве этого дополнительного параметра, и затем их сигнатура конструктора будет проверена на совместимость с сигнатурой базового класса:

abstract class HDWallet<T extends typeof HDWallet> {
  /**
   * Return BIP39 12 words new random mnemonic seed phrase
   */
  static generateMnemonic(): string {
    return "";
  }

  constructor(bip39SeedPhrase: string, password?: string, testnet?: boolean) { }

  abstract getAddress(addressIndex?: number) : string;

  abstract async getBalance(address? : string) : Promise<number>; 


}


class BitcoinHDWallet extends HDWallet<typeof BitcoinHDWallet> {
  getAddress(addressIndex?: number) : string { return ""}

  async getBalance(address?: string): Promise<number> { return Promise.resolve(0);} 
}
class EthereumHDWallet extends HDWallet<typeof EthereumHDWallet> { /// error

  constructor(testnet?: boolean) { 
    super("", "", false)
  }
  getAddress(addressIndex?: number) : string { return ""}

  async getBalance(address?: string): Promise<number> { return Promise.resolve(0);} 
}

Обратите внимание, что используя интерфейсы, вы можете описать статическую часть класса, а также тип экземпляра, в зависимости от того, что вы пытаетесь проверить:

interface HDWalletClass {
  generateMnemonic(): string 

  new (bip39SeedPhrase: string, password?: string, testnet?: boolean): {

    getAddress(addressIndex?: number): string;

    getBalance(address?: string): Promise<number>;
  }
}
abstract class HDWallet {
  /**
   * Return BIP39 12 words new random mnemonic seed phrase
   */
  static generateMnemonic(): string {
    return "";
  }

  constructor(bip39SeedPhrase: string, password?: string, testnet?: boolean) { }

  abstract getAddress(addressIndex?: number) : string;

  abstract async getBalance(address? : string) : Promise<number>; 


}


class BitcoinHDWallet extends HDWallet {
  getAddress(addressIndex?: number) : string { return ""}

  async getBalance(address?: string): Promise<number> { return Promise.resolve(0);} 
}
class EthereumHDWallet extends HDWallet { /// error

  constructor(testnet?: boolean) { 
    super("", "", false)
  }
  getAddress(addressIndex?: number) : string { return ""}

  async getBalance(address?: string): Promise<number> { return Promise.resolve(0);} 
}
let a: HDWalletClass = BitcoinHDWallet; //ok
let b: HDWalletClass = EthereumHDWallet; // err
...