【问题标题】:How to implement a Typescript interface to an es5-style "class"?如何为 es5 风格的“类”实现 Typescript 接口?
【发布时间】:2018-07-09 00:50:07
【问题描述】:

我们可以为 es6 类实现接口的方式非常简单:

interface IDog {
    bark(): void
}

class Dog implements IDog {
    bark(): void {

    }
}

问题是:如何对这个“类”实现相同的接口:

const Dog = function() {

}

Dog.prototype.bark = function() {

}

我尝试将 Dog 的类型定义为 IDog:const Dog: IDog。没用。

所以,我需要它来实现依赖反转,但我不知道如何使用 es5 类来做到这一点。我看到 Classical Inheritance 风格是 Javascript 中的“反模式”,所以我决定用旧的方式创建类,并且需要帮助实现 Typescript 接口。

【问题讨论】:

  • 为什么不在编译时使用 typescript 类和目标 es5?顺便说一句,编译后每个接口/类型都会被删除,因此无法在运行时解析 arg 类型

标签: javascript typescript dependency-inversion


【解决方案1】:

我假设您想要 es5 样式的类实现,它被声明为符合 IDog 接口,并由编译器进行类型检查以确保它确实符合该接口。

坏消息 - TypeScript 不支持。你可以让 TypeScript 认为 es5 Dog 是一个实现了IDog 的类,但是你必须声明DogConstructor 接口并为Dog 使用as any as DogConstructor 类型断言。而且你不能让 TypeScript 对基于原型的实现进行类型检查,因为Object.prototype(以及随后的Dog.prototype)在系统库中被声明为any(有关一些讨论,请参见theseissues):

interface IDog {
    bark(): void
}

interface DogConstructor {
    new(): IDog;
}

const Dog = function (this: IDog) {
    // this.bark(); you can do this because of `this: IDog` annotation
} as any as DogConstructor;

Dog.prototype.bark = function() {

}

const p = new Dog();
p.bark();

我认为对此的支持不会得到改善。 Es5 风格的类通常是在不进行类型检查的 javascript 代码中实现的,TypeScript 为编写类型声明提供了足够的支持,允许以类型安全的方式使用 javascript 实现。如果你在 TypeScript 中实现类,你可以简单地使用真正的类。

【讨论】:

    【解决方案2】:

    对此没有语言支持,如果这种情况足够普遍,我们能做的最好的事情就是推出我们自己的类创建函数,这对我们添加到类中的成员施加限制。

    使用noImplicitThis 编译器选项和ThisType 我们也可以对类成员进行很好的类型检查,我们没有得到任何像明确的字段分配这样的花哨的东西,但这已经足够了:

    interface IDog {
        bark(): void
    }
    
    function createClass<TInterfaces, TFields = {}>() {
        return function<TMemebers extends TInterfaces>(members: TMemebers & ThisType<TMemebers & TFields>) {
            return function<TCtor extends (this: TMemebers & TFields, ...a: any[]) => any>(ctor: TCtor) : FunctionToConstructor<TCtor, TMemebers & TFields> {
                Object.assign(ctor.prototype, members);
                return ctor as any;
            }
        }
    }
    
    const Dog = createClass<IDog, { age: number }>()({
        eat() {
            // this is not any and has the fields defined in the TFields parameter
            // and the methods defined in the current object literal
            for(let i =0;i< this.age;i++) {
                this.bark();
                console.log("eat")
            }
        },
        bark() {
            console.log("BA" + "R".repeat(this.age) + "K");
        }
    })(function(age: number) {
        this.age = age; // this has the fields and members previously defined 
        this.bark();
    })
    const p = new Dog(10);
    p.bark();
    
    // Helpers
    type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
    
    type FunctionToConstructor<T, TReturn> =
        T extends (a: infer A, b: infer B) => void ?
        IsValidArg<B> extends true ? new (p1: A, p2: B) => TReturn :
        IsValidArg<A> extends true ? new (p1: A) => TReturn :
        new () => TReturn :
        never;
    

    注意上面的实现类似于答案here,你可以在那里阅读更深入的解释。

    【讨论】:

      猜你喜欢
      • 2021-02-21
      • 2019-06-27
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 2019-03-04
      相关资源
      最近更新 更多