Me gusta mucho la característica del método de extensión de C#. Estaba tratando de experimentarlo en Typescript con decoradores.
export function extension ( ctr: any ) { let originalFunction: Function ; return function ( target, propertyKey: string , descriptor: PropertyDescriptor ) { originalFunction = descriptor.value; ctr.prototype[propertyKey] = function ( ...args ) { return originalFunction( this , ...args); } } }
Creé una fábrica de decoradores llamada
extension
. Esto toma la clase a la que queremos agregar el método. Esencialmente, lo que queremos es ejecutar este método en cualquier objeto que sea una instancia de " ctr
".Dado que Typescript no nos brinda un concepto de método de extensión de compilación, como solución alternativa, estamos agregando la función al prototipo de la clase pasada.
export class User { constructor ( public name: string = 'joe', public lastname: string = 'doe' ){} } export class Extensions { @extension(User) static test(user: User){ console .log(user.name , 'this works' ); } @extension(User) static gaveMoney(thisArg: User, amount : number, to : User){ console .log( ` ${thisArg.name} gave ${to.name} \$ ${amount} ` ); } }
const user = new User(); const user2 = new User( 'mehmet' , '' ); user.gaveMoney( 10 , user2); user2.gaveMoney( 20 , user);
user.test();
Como puede ver, los métodos con los que decoramos
extension
se añaden al prototipo de User
clase.Si está recibiendo un error
Property 'gaveMoney' does not exist on type 'User'
puede hacer una de las dos cosas siguientes: (user as any).gaveMoney( 10 , user2);
o
export interface User{ test; // adding the name only will fix the error gaveMoney(n: number, u :User) // this will give intellisense }
Al mutar el prototipo esencialmente estamos contaminando el prototipo. Así que este truco no funcionará en
frozen
o sealed
objetos. Veamos lo siguiente como ejemplo. Congelamos el User2
class y su prototipo y obtuve un error porque nuestro método no pudo modificar la clase. export class Extensions2 { @extension(User2) static test(user: User2){ console .log(user.name , 'this works' ); } } export class User2 { name = 'john' } Object .freeze(User2); Object .freeze(User2.prototype); const sealedUser = new User2(); sealedUser.test();
Aunque este truco funcionará para nuestro material, probablemente no funcionará en la mayoría de las bibliotecas de terceros, suponiendo que estén protegidas contra la contaminación de prototipos. La belleza de los métodos de extensión es que deberían funcionar en clases sobre las que no tenemos ningún control.
Esperemos que el equipo de Typescript decida agregar esta característica increíble. ( https://github.com/microsoft/TypeScript/issues/9 )
Demostración de este artículo: https://stackblitz.com/edit/ts-ms-extension-methods-experiment?file=index.ts