Extendiendo el tipo Vue para usar tus plugins en TypeScript

Uno de los problemas que más nos hacen explotar la cabeza son los ‘typings‘ de TypeScript por suerte librerías como Vue ya los traen de serie, además de incluir algunos decoradores para facilitarnos el trabajo.

Aunque tenemos un pequeño problema cuando decidimos usar plugins de terceros o nuestros propios plugins, TypeScript no reconoce el plugin cómo propiedad de uno de nuestros componentes, veamos un ejemplo:

Tengo un proyecto hecho con Vue donde he instalado el plugin ‘Vue Analytics’ para integrarme con Google Analytics, pero este plugin está tipado y cuando decido utilizarlo en mi componente mi editor me muestra un error.

Solución rápida

Todos hemos tenido prisa para solucionar algo y en vez de investigar decidimos temporlamente (Si.. temporalmente) arreglar esto de forma rápida, si nos encontramos en uno de esos casos seguramente obtaremos por una solución tal como esta, que consiste en declarar una propiedad en el mismo componente de manera que exista en TypeScript y en JavaScript.


export default class extends Vue {
  public $ga: any;
    
    public created(): void {
      this.$ga.event('item', 'click', 'serviceworker-download');
    }
}

Perfecto esto funciona, pero es una chapuza. Podemos pensar en crear un componente Base del que extiendan el resto de componentes de manera que esto sea reutilizable. Aunque sigue sin ser la mejor solución. Tampoco sería una mala decisión crear una interfaz Base o una interfaz por cada plugin que vayamos a necesitar.


// BaseComponent.ts
export default class extends Vue {
  public $ga: any;
}

// MyComponent.ts
export default class extends BaseComponent {
  public created(): void {
      this.$ga.event('item', 'click', 'serviceworker-download');
    }
}

// GAPlugin.ts
export default interface GAPlugin {
  public $ga: any;
}

// MyComponent.ts
export default class extends Vue implements GAPlugin  {
  public created(): void {
      this.$ga.event('item', 'click', 'serviceworker-download');
    }
}

Solución utilizando ‘augmenting types’

La misma documentación de Vue (Si es que a veces buscando 5 minutos encuentras la solución correcta…) nos propone utilizar el ‘module augmentation’ para solucionar esto de una forma mucho más limpia.

Primero de todo creamos una carpeta typings en la raíz de nuestro proyecto (TypeScript debería incluirla automáticamente junto al resto de typings) y dentro un archivo donde vamos a declarar los plugins extendiendo los typings de Vue, en mi caso le he llamado vue.d.ts

Y dentro de este archivo añadimos el código que nos daba la documentación, añadiendo las propiedades que necesitemos.


// 1. Make sure to import 'vue' before declaring augmented types
import Vue from 'vue'

// 2. Specify a file with the types you want to augment
//    Vue has the constructor type in types/vue.d.ts
declare module 'vue/types/vue' {
  // 3. Declare augmentation for Vue
  interface Vue {
    $ga: any;
  }
}

Bonus, utilizando menos ‘any’

Recuerda que siempre es una buena práctica intentar usar el mínimo el tipo ‘any’ así que podemos crear interfaces o declarar tipos con la estructura deseada y añadirlos.
Además, algunas veces los plugins ya vienen con sus tipos así que puedes añadirlos si lo consideras necesario.


// 1. Make sure to import 'vue' before declaring augmented types
import Vue from 'vue'
import VueRouter from 'vue-router';
import GA from '~/plugins/ga';

// 2. Specify a file with the types you want to augment
//    Vue has the constructor type in types/vue.d.ts
declare module 'vue/types/vue' {
  // 3. Declare augmentation for Vue
  interface Vue {
    $ga: GA;
    $router: VueRouter;
  }
}

¿Y a ti? ¿Te ha pasado alguna vez? ¿Se te ocurren más soluciones que quieras compartir?

I use JavaScript, TypeScript, .NET, Node, Cordova and SASS for Web and App development, working at Plain Concepts and HelpDev founder.

Related Posts