如何实现 es5 风格的 Typescript 接口 "class"?
How to implement a Typescript interface to an es5-style "class"?
我们实现 es6 接口的方式 class 非常简单:
interface IDog {
bark(): void
}
class Dog implements IDog {
bark(): void {
}
}
问题是:如何实现与此相同的接口 "class":
const Dog = function() {
}
Dog.prototype.bark = function() {
}
我尝试将狗的类型定义为 IDog:const Dog: IDog
。没用。
所以,我需要它来实现依赖倒置,但我不知道如何使用 es5 classes 来实现。我看到经典继承样式是 Javascript 中的 "antipattern",所以我决定以旧方式创建 classes 并需要帮助实现它们的 Typescript 接口。
我假设您想要 es5 风格的 class 实现,它被声明为符合 IDog
接口,并由编译器进行类型检查以确保它确实符合该接口。
坏消息 - TypeScript 不支持。您可以让 TypeScript 认为 es5 Dog
是实现 IDog
的 class,但是您必须声明 DogConstructor
接口并使用 as any as DogConstructor
类型断言 Dog
。而且您不能使 TypeScript 对基于原型的实现进行类型检查,因为 Object.prototype
(以及随后的 Dog.prototype
)在系统库中被声明为 any
(请参阅 these issues 进行一些讨论):
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 风格的 classes 通常在 javascript 代码中实现,这并不意味着要进行类型检查,TypeScript 为编写类型声明提供了足够的支持,允许在类型安全中使用 javascript 实现方法。如果你在 TypeScript 中实现 classes,你可以简单地使用真正的 classes.
对此没有语言支持,如果这种情况很常见,我们能做的最好的事情就是推出我们自己的 class 创建功能,对我们添加的成员施加限制class.
使用 noImplicitThis
编译器选项和 ThisType
我们也可以对 class 成员进行很好的类型检查,我们不会得到任何花哨的东西,比如明确的字段分配, 但它已经足够好了:
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;
注意上面的实现与答案类似,您可以在那里阅读更深入的解释。
我们实现 es6 接口的方式 class 非常简单:
interface IDog {
bark(): void
}
class Dog implements IDog {
bark(): void {
}
}
问题是:如何实现与此相同的接口 "class":
const Dog = function() {
}
Dog.prototype.bark = function() {
}
我尝试将狗的类型定义为 IDog:const Dog: IDog
。没用。
所以,我需要它来实现依赖倒置,但我不知道如何使用 es5 classes 来实现。我看到经典继承样式是 Javascript 中的 "antipattern",所以我决定以旧方式创建 classes 并需要帮助实现它们的 Typescript 接口。
我假设您想要 es5 风格的 class 实现,它被声明为符合 IDog
接口,并由编译器进行类型检查以确保它确实符合该接口。
坏消息 - TypeScript 不支持。您可以让 TypeScript 认为 es5 Dog
是实现 IDog
的 class,但是您必须声明 DogConstructor
接口并使用 as any as DogConstructor
类型断言 Dog
。而且您不能使 TypeScript 对基于原型的实现进行类型检查,因为 Object.prototype
(以及随后的 Dog.prototype
)在系统库中被声明为 any
(请参阅 these issues 进行一些讨论):
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 风格的 classes 通常在 javascript 代码中实现,这并不意味着要进行类型检查,TypeScript 为编写类型声明提供了足够的支持,允许在类型安全中使用 javascript 实现方法。如果你在 TypeScript 中实现 classes,你可以简单地使用真正的 classes.
对此没有语言支持,如果这种情况很常见,我们能做的最好的事情就是推出我们自己的 class 创建功能,对我们添加的成员施加限制class.
使用 noImplicitThis
编译器选项和 ThisType
我们也可以对 class 成员进行很好的类型检查,我们不会得到任何花哨的东西,比如明确的字段分配, 但它已经足够好了:
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;
注意上面的实现与答案