programación

Soporte para TypeScript en Vuejs

Este artículo es una traducción de la documentación oficial. Mis notas estarán debidamente identificadas.

Soporte para TypeScript

A partir de la versión de Vue 2.5.0 en adelante se ha mejorado significativamente las declaraciones de tipos para que operen con el API default basado en objectos. Al mismo tiempo se ha introducido algunos cambios que requieren se realice una debida actualización. Leer el siguiente artículo para más detalles.

Nota: Con esta introducción se reafirma el compromiso del equipo de Vue para mantener un ambiente saludable y ágil a la hora de trabajar con TypeScripte dentro de Vue.

Declaración oficial en los paquetes de NPM

Un sistema de tipado estático puede ayudar a prevenir muchos potenciales errores de runtime, especialmente cuando la aplicación continúa creciendo. Es por esto que Vue ofrece soporte oficial a las declaraciones para tipado estático en TypeScript – no solo para el core de Vue, sino también para las librerías vue-router y vuex.

Dado que estas están publicadas en NPM, y la versión más reciente de TypeScript sabe como resolver las declaraciones de tipo en los paquetes de NPM, esto significa que cuando se instala a través de NPM, no se necesita ninguna herramienta adicional para habilitar TypeScript en Vue.

Nota: Actualmente TypeScript cuenta con una rica colección de tipos gracias al esfuerzo del colectivo Definitely Typed y a la aceptación en general de la comunidad.

Configuración recomendada

// tsconfig.json
{
  "compilerOptions": {
    // esto se alinea con el soporte de Vue en el navegador 
    "target": "es5",
    // permite una inferencia más estricta para las propiedades de datos en `this`
    "strict": true,
    // si se usa webpack 2+ o rollup, para aprovechar el método tree shaking:
    "module": "es2015",
    "moduleResolution": "node"
  }
}

Tenga en cuenta que debe incluir strict: true (o al menos noImpllicitThis: true, que es parte del flag strict) para aprovechar la verificación de tipo para this en los métodos de los componentes, de lo contrario, siempre se tratará como tipo any.

Consulte la documentación de TypeScript para más detalles sobre las opciones del compilador.

Herramientas para desarrollo

Creación de proyectos

Vue CLI 3 puede generar proyectos compatibles con TypeScript. Para comenzar:

# 1. Instalar Vue CLI si aún no lo ha instalado
npm install --global @vue/cli

# 2. Crear un nuevo proyecto, luego seleccione la opción "Manually select features"
vue create mi-proyecto

Soporte en el editor

Para desarrollar aplicaciones usando Vue y TypeScript, se recomienda usar Visual Studio Code, el cual cuenta con soporte nativo para TypeScript. Si usas componentes en modo sigle-file (SFCs), instala la increíble extensión Vetur, que proporciona inferencia de TypeScript dentro del SFC y ofrece una gran cantidad de excelente opciones.

WebStorm también cuenta con soporte nativo para ambos, TypeScript y Vue.

Nota: Vetur es una estupenda herramienta para Vue. Aún si no deseas utilizar TypeScript es una extensión que deberías explorar y añadir al repertorio.

Uso básico

Para que TypeScript infiera adecuadamente los tipos dentro de un componente en Vue, necesitas definir los componentes usando Vue.component o Vue.extend:

import Vue from 'vue'

const Component = Vue.extend({
  // inferencia de tipo habilitada
})

const Component = {
  // aquí NO habrá inferencia de tipos,
  // porque TypeScript no puede inferir que esto son opciones para un componente Vue.
}

Componentes Vue utilizando el modelo Class-Style

Si prefieres una API basada en clases para declarar componentes, puedes usar el decorador oficial vue-class-component:

import Vue from 'vue'
import Component from 'vue-class-component'

// El decorador @Component le indica a la clase que es un componente Vue
@Component({
  // Todas las opciones del componente se definen aquí
  template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
  // La data inicial puede ser declarada como propiedades de la instancia
  message: string = 'Hello!'

  // Los métodos del componente pueden ser declarado como métodos de la instancia
  onClick (): void {
    window.alert(this.message)
  }
}

Nota: Los decoradores usan una sintaxis especial a través del símbolo @ y se colocan inmediatamente antes de que se decore el código. En la programación orientada a objetos, el patrón decorador (también conocido como Wrapper, nombre alternativo al patrón Adapter) es un patrón de diseño que permite agregar comportamiento a un objeto individual, ya sea estática o dinámicamente, sin afectar el comportamiento de otros objetos de la misma clase. Al momento de redactar este artículo, JavaScript no cuenta con apoyo nativo para decoradores. Existe la propuesta para añadirlo al spec y actualmente se encuentra en el borrador, “Stage 2 Draft / May 23, 2018”.

Aumento de tipos para uso en plug-ins

Los plug-ins pueden agregarse a las propiedades globales/instancia de Vue y a las opciones de los componentes. En estos casos, se necesitan declaraciones de tipo para hacer que los plug-ins se compilen en TypeScript. Afortunadamente, hay una característica de TypeScript para aumentar los tipos existentes llamada aumento de módulos (module augmentation.).

Por ejemplo, para añadir una propiedad a la instancia llamada $myProperty de tipo cadena:

// 1. Asegurate importar 'vue' antes de la declaración aumentada para los tipos
import Vue from 'vue'

// 2. Especifique el archivo con los tipos que desea aumentar 
//    Para el constructor Vue este se encuentra en types/vue.d.ts
declare module 'vue/types/vue' {
  // 3. Utilice la propiedad aumentada en Vue
  interface Vue {
    $myProperty: string
  }
}

Luego de incluir el código anterior como un archivo de declaraciones (algo parecido a my-property.d.ts) en tu proyecto, puedes usar myProperty en la instancia de Vue.

var vm = new Vue()
console.log(vm.$myProperty) // Esto debería compilar sin problema

También puedes declarar propiedades globales adicionales y opciones en los componentes:

import Vue from 'vue'

declare module 'vue/types/vue' {
  // Propiedades Globales pueden ser definidas
  // en la interface `VueConstructor`
  interface VueConstructor {
    $myGlobal: string
  }
}

// ComponentOptions se define en types/options.d.ts
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    myOption?: string
  }
}

Las declaraciones anteriores permiten que el siguiente código pueda ser compilado:

// Propiedad Global
console.log(Vue.$myGlobal)

// Opción adicional
var vm = new Vue({
  myOption: 'Hello'
})

Anotación de tipos para resultados

Debido a la naturaleza circular de los archivos de declaración de Vue, TypeScript puede tener dificultades para inferir los tipos de ciertos métodos. Por esta razón, es posible que deba anotar el tipo del resultado en métodos como render y computed.

import Vue, { VNode } from 'vue'

const Component = Vue.extend({
  data () {
    return {
      msg: 'Hello'
    }
  },
  methods: {
    // necesita una anotación debido al uso de `this` en el resultado
    greet (): string {
      return this.msg + ' world'
    }
  },
  computed: {
    // necesita una anotación
    greeting(): string {
      return this.greet() + '!'
    }
  },
  // `createElement` es inferido pero `render` necesita la anotación
  render (createElement): VNode {
    return createElement('div', this.greeting)
  }
})

Si encuentra que la inferencia de tipo o la resolución del método no funciona, anotar ciertos métodos puede ayudar a resolver estos problemas. Usar la opción --noImplicitAny ayudará a encontrar muchos de estos métodos no anotados.

Nota: Es importante destacar que el uso de tipos no es un requisito para todos los proyectos. Utilizar TypeScript tampoco lo es. Las ventajas son inmensas pero la curva de aprendizaje puede ser atropellada si no se evalúa correctamente al momento de introducirlo en el proyecto o al equipo de trabajo. Tómese su tiempo y no tema en experimentar con esta tecnología.