基于TypeScript接口的通用工厂

时间:2017-09-29 20:59:57

标签: typescript

我想实现泛型方法,它将根据提供的接口返回具体类型。这在TypeScript中是否可行?

这是我的伪示例:

class Account {
    private int id = 0;
    private double balance = 0.0;
    private double annualInterestRate = 0.0;
    private java.util.Date dateCreated;

// set dateCreated for the time and date the account was created
Account() {
    dateCreated = new java.util.Date();
}

// Set accounts id and balance equal to this objects
Account(int id, double balance){
    this();
    this.id = id;
    this.balance = balance;
}

// Getters and Setters manipulating variables to be set to the created account
// Setters have no return value and set itself equal to the variable and getters 
// grab the variables and return them.
public int getId() {
    return this.id;
}

public double getBalance() {
    return this.balance;
}

public double getannualInterestRate() {
    return this.annualInterestRate;
}

public String getDateCreated() {
    return this.dateCreated.toString();
}

public void setId(int id) {
    this.id = id;
}

public void setBalance(double balance) {
    this.balance = balance;
}

public void setannualInterest(double annualInterestRate) {
    this.annualInterestRate = annualInterestRate;
}

public double getMonthlyInterestRate() {
    return (annualInterestRate / 100) / 12;
}

public double getMonthlyInterest() {
    return balance * getMonthlyInterestRate();
}

// set balance of withdraw to balance - amount = balance
public void withdraw (double amount) {
    if(amount < balance) {
        balance -= amount;
    }
}

// set balance of deposit to balance + amount = balance
public void deposit(double amount) {
    balance += amount;
    //Transaction.add(new Transaction('D', amount, balance, "Deposit to account"));
}   

@Override
public String toString() {
    return "Account holder name: " + CustomerAccount.name + 
            "\nAccount id: " + id + 
            "\nAnnual interest: " + this.getannualInterestRate() + 
            "\nMonthly Interest Rate: " + this.getMonthlyInterestRate() + 
            "\nBalance " + this.getBalance() +
            "\nTransaction: ";
}
}

interface IDog { canRun: boolean; } interface IBird { canFly: boolean; } class Dog implements IDog { canRun: boolean; } class Bird implements IBird { canFly: boolean; } function createInstance<T>(): T { const key = typeof T; switch (key) { case IBird: return new Bird(); return case IDog: return new Dog(); default: break; } } // concrete is Dog const concrete = createInstance<IDog>(); 方法是我想要实现的原始示例,它不会编译!

我想为createInstance()方法提供接口类型,并实现一些逻辑,这些逻辑将为提供的接口创建具体类型。

TypeScript可以吗?

4 个答案:

答案 0 :(得分:1)

当然,您可以在TypeScript中执行工厂方法。一种方法:

interface IAnimal {

}

interface IDog extends IAnimal {
    canRun: boolean;
}

interface IBird extends IAnimal {
    canFly: boolean;
}

class Dog implements IDog {
    canRun: boolean;
}

class Bird implements IBird {
    canFly: boolean;
}

class AnimalFactory {
    public static create(animalType: string): IAnimal {
        switch(animalType) {
            case 'dog':
                return new Dog();
            case 'bird':
                return new Bird();
            default:
                throw new Error('Wrong animal type.');
        }
    }
}

const dog: IDog = AnimalFactory.create('dog');
const bird: IBird = AnimalFactory.create('bird');

请注意,工厂隐藏了实际的类名/实现,并通过接口进行操作。如果您愿意并使用构建器模式返回与IAnimal界面兼容的动物,您可以更进一步,例如

case 'dog':
    const dog: IDog = new DogBuilder()
        .setColor('white')
        .setWeight(30)
        .build() // whatever, you get the idea
    return dog;

答案 1 :(得分:1)

您需要使用重载:

function createInstance(key: 'dog'): Dog;
function createInstance(key: 'bird'): Bird;
function createInstance(key: 'dog' | 'bird') {
  // implement
}

然后将密钥作为参数传递

const dog = createInstance('dog'); // typeof dog is Dog

答案 2 :(得分:0)

在运行时无法访问接口,但您可以使用添加类型检查 String Literal Types

interface IDog {
    canRun: boolean;
}

interface IBird {
    canFly: boolean;
}

class Dog implements IDog {
    canRun: boolean;
}

class Bird implements IBird {
    canFly: boolean;
}

function createInstance(type: 'bird'): Bird;
function createInstance(type: 'dog'): Dog;
function createInstance(type: string): any {
  switch (type) {
    case 'bird':
        return new Bird();
    case 'dog':
        return new Dog();
  }
}

// concrete is Dog
const concrete = createInstance('dog');

另外我认为工厂返回类型应该是接口:

function createInstance(type: 'bird'): IBird;
function createInstance(type: 'dog'): IDog;

<强>更新

另一种方法是存储对类的引用:

interface IInterfaces {
    dog: IDog;
    bird: IBird;
}

type IFactory<T> = {
    [P in keyof T]: new() => T[P];
}

let factory: IFactory<IInterfaces> = {
    dog: Dog,
    bird: Bird
}

let dog = new factory.dog();

答案 3 :(得分:0)

问题在于TS接口在运行时不可用,因此您不能使用JS创建接口。但!它们存在于代码中,因此您可以读取文件并使用使用文件系统的解决方案来解决此问题。

最近我发现了这个:https://github.com/google/intermock,它似乎真的很有用。我还没有尝试过,但这是我找到的唯一解决此问题的方法。

node build/src/cli/index.js --files ./example-file.ts --interfaces "Admin"

您选择接口所在的文件以及要从中创建对象的接口。

您可以在测试之前使用诸如husky之类的命令运行命令,以便在实际测试之前生成模拟