javascript - ¿Cómo implementar una interfaz de Typescript en una "clase" al estilo es5?
La forma en que podemos implementar una interfaz en una clase ES6 es bastante sencilla:
interface IDog {
bark(): void
}
class Dog implements IDog {
bark(): void {
}
}
La pregunta es: ¿cómo implementar la misma interfaz en esta "clase":
const Dog = function() {
}
Dog.prototype.bark = function() {
}
Intenté definir el tipo de Perro como IDog: const Dog: IDog. No funcionó.
Entonces, necesito implementarlo para la inversión de dependencias y no puedo entender cómo hacerlo con clases ES5. Vi que el estilo de herencia clásica es un "antipatrón" en Javascript, así que decidí crear clases a la manera antigua y necesito ayuda para implementar interfaces de Typescript en ellas.
Respuesta #1
Asumo que deseas una implementación de clase al estilo ES5, que se declare para cumplir con la interfaz IDog, y que sea verificada por el compilador para asegurar que realmente cumple con esa interfaz.
Malas noticias: TypeScript no admite eso. Puedes hacer que TypeScript piense que es5 Dog es una clase que implementa IDog, pero debes declarar la interfaz DogConstructor y usar la aserción de tipo as any as DogConstructor para Dog. Y no puedes hacer que TypeScript verifique con tipado la implementación basada en prototipos porque Object.prototype (y, posteriormente, Dog.prototype) se declara como any en la biblioteca del sistema (consulta estos problemas para algo de discusión):
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();
No creo que el soporte para esto alguna vez mejore. Las clases al estilo ES5 suelen implementarse en código JavaScript que no está pensado para ser verificado por tipos, y TypeScript proporciona suficiente soporte para escribir declaraciones de tipos que permitan usar la implementación de JavaScript de manera segura para tipos. Si estás implementando clases en TypeScript, simplemente puedes usar clases reales.
Respuesta #2
No hay soporte de idioma para esto, lo mejor que podemos hacer, si esto ocurre con suficiente frecuencia, es implementar nuestra propia función de creación de clases, que imponga restricciones sobre los miembros que agregamos a la clase.
Usando la opción de compilador noImplicitThis y ThisType también podemos obtener una verificación de tipos bastante buena para los miembros de la clase, no obtenemos ninguna de las funciones avanzadas como la asignación definitiva de campos, pero es lo suficientemente bueno:
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;
Nota La implementación anterior es similar a la respuesta aquí y puedes leer una explicación más detallada de la misma allí.
