Integrando TypeScript con Svelte

Svelte es uno de los marcos de JavaScript más nuevos y su popularidad está aumentando rápidamente. Es un marco basado en plantillas, pero que permite JavaScript arbitrario dentro de los enlaces de la plantilla; tiene una magnífica historia de reactividad que es simple, flexible y eficaz; y como marco compilado con anticipación (AOT), tiene un rendimiento y tamaños de paquetes increíblemente impresionantes. Esta publicación se centrará en la configuración de TypeScript dentro de las plantillas de Svelte. Si eres nuevo en Svelte, te recomiendo que consultes el tutorial introductorio y los documentos .

Si desea seguir el código (o desea depurar lo que podría faltar en su propio proyecto), puede clonar el repositorio . Tengo ramas instaladas para demostrar las distintas piezas que repasaré.

Nota: Si bien vamos a integrar manualmente Svelte y Typecript, podrías considerar usar la plantilla oficial de Svelte que hace lo mismo si estás iniciando un proyecto totalmente nuevo. De cualquier manera, esta publicación cubre una configuración de TypeScript que sigue siendo relevante, incluso si usa la plantilla.

Configuración básica de TypeScript y Svelte

Veamos una configuración básica. Si va a la initial-setuprama en el repositorio, hay un proyecto Svelte simple configurado, con TypeScript. Para ser claros, TypeScript solo funciona en archivos independientes .ts. No está integrado de ninguna manera en Svelte. Lograr la integración de TypeScript es el propósito de esta publicación.

Repasaré algunas piezas que hacen que Svelte y TypeScript funcionen, principalmente porque las cambiaré en un momento para agregar compatibilidad con TypeScript a las plantillas de Svelte.

Primero, tengo un tsconfig.jsonarchivo:

{  "compilerOptions": {    "module": "esNext",    "target": "esnext",    "moduleResolution": "node"  },  "exclude": ["./node_modules"]}

Este archivo le dice a TypeScript que quiero usar JavaScript moderno, usar resolución de nodo y excluir a node_modulesde la compilación.

Entonces, typings/index.d.tstengo esto:

declare module "*.svelte" {  const value: any;  export default value;}

Esto permite que TypeScript coexista con Svelte. Sin esto, TypeScript emitiría errores cada vez que se carga un archivo Svelte con una declaración de importación. Por último, debemos indicarle a webpack que procese nuestros archivos Svelte, lo cual hacemos con esta regla en webpack.config.js:

{  test: /.(html|svelte)$/,  use: [    { loader: "babel-loader" },    {      loader: "svelte-loader",      options: {        emitCss: true,      },    },  ],}

Todo eso es la configuración básica para un proyecto que utiliza componentes Svelte y archivos TypeScript. Para confirmar que todo se compila, abra un par de terminales y ejecútelo npm starten uno, lo que iniciará una observación del paquete web y npm run tscwen el otro, para iniciar una tarea de observación de TypeScript. Esperemos que ambos se ejecuten sin errores. Para verificar realmente que la verificación de TypeScript se esté ejecutando, puede cambiar:

let x: number = 12;

…en index.ts:

let x: number = "12";

…y ver aparecer el error en el reloj de TypeScript. Si realmente desea ejecutar esto, puede ejecutarlo node serveren una tercera terminal (recomiendo iTerm2 , que le permite ejecutar estas terminales dentro de pestañas en la misma ventana) y luego presionar localhost:3001.

Agregar TypeScript a Svelte

Agreguemos TypeScript directamente a nuestro componente Svelte y luego veamos qué cambios de configuración necesitamos para que funcione. Primero vaya a Helper.sveltey agréguelo lang="ts"a la etiqueta del script. Eso le dice a Svelte que hay TypeScript dentro del script. Ahora agreguemos algo de TypeScript. Cambiemos el valaccesorio que se verificará como un número, a través de export let val: number;. Todo el componente ahora se ve así:

script  export let val: number;/scripth1Value is: {val}/h1

La ventana de nuestro paquete web ahora debería tener un error, pero eso es lo esperado.

Necesitamos decirle al cargador Svelte cómo manejar TypeScript. Instalemos lo siguiente:

npm i svelte-preprocess svelte-check --save

Ahora, vayamos a nuestro archivo de configuración de paquete web y tomemos svelte-preprocess:

const sveltePreprocess = require("svelte-preprocess");

…y agréguelo a nuestro svelte-loader:

{  test: /.(html|svelte)$/,  use: [    { loader: "babel-loader" },    {      loader: "svelte-loader",      options: {        emitCss: true,        preprocess: sveltePreprocess({})      },    },  ],}

Bien, reiniciemos el proceso del paquete web y debería compilarse.

Agregar cheque

Hasta ahora, lo que tenemos se construye, pero no se verifica. Si tenemos código no válido en un componente de Svelte, queremos que genere un error. Entonces, vayamos a App.svelte, agreguemos lo mismo lang="ts"a la etiqueta script y luego pasemos un valor no válido para la valpropiedad, como este:

Helper val={"3"} /

Si miramos en nuestra ventana de TypeScript, no hay errores, pero debería haberlos. Resulta que no escribimos check nuestra plantilla Svelte con el compilador tsc normal, sino con la utilidad svelte-check que instalamos anteriormente. Detengamos nuestra observación de TypeScript y, en esa terminal, ejecutemos npm run svelte-check. Eso iniciará el proceso de verificación esbelta en el modo de vigilancia y deberíamos ver el error que esperábamos.

Ahora, elimine las comillas alrededor de 3, y el error debería desaparecer:

¡Limpio!

En la práctica, queremos que svelte-check y tsc se ejecuten al mismo tiempo para detectar ambos errores tanto en nuestros archivos TypeScript como en nuestras plantillas Svelte. Hay un montón de utilidades en npm que permiten hacer esto, o podemos usar iTerm2, que puede dividir múltiples terminales en la misma ventana. Lo estoy usando aquí para ejecutar el servidor, la compilación del paquete web, la compilación tsc y la compilación svelte-check.

Esta configuración está en la basic-checkingrama del repositorio.

Atrapando accesorios faltantes

Todavía hay un problema que debemos resolver. Si omitimos una propiedad requerida, como la valpropiedad que acabamos de ver, aún así no obtendremos un error, pero deberíamos hacerlo, ya que no le asignamos un valor predeterminado en Helper.sveltey, por lo tanto, es obligatorio.

Helper / // missing `val` prop

Para indicarle a TypeScript que informe esto como un error, volvamos a nuestro tsconfigy agreguemos dos valores nuevos.

"strict": true,"noImplicitAny": false 

El primero habilita una serie de comprobaciones de TypeScript que están deshabilitadas de forma predeterminada. El segundo noImplicitAny, desactiva uno de esos controles estrictos. Sin esa segunda línea, cualquier variable que carezca de un tipo (que implícitamente se escribe como any) ahora se informa como un error (no hay ninguno implícito, ¿entiendes?)

Opinions differ widely on whether noImplicitAny should be set to true. I happen to think it’s too strict, but plenty of people disagree. Experiment and come to your own conclusion.

Anyway, with that new configuration in place, we should be able to restart our svelte-check task and see the error we were expecting.

This setup is in the better-checking branch of the repo.

Odds and ends

One thing to be aware of is that TypeScript’s mechanism for catching incorrect properties is immediately, and irreversibly switched off for a component if that component ever references $$props or $$restProps. For example, if you were to pass an undeclared prop of, say, junk into the Helper component, you’d get an error, as expected, since that component has no junk property. But this error would immediately go away if the Helper component referenced $$props or $$restProps. The former allows you to dynamically access any prop without having an explicit declaration for it, while $$restProps is for dynamically accessing undeclared props.

This makes sense when you think about it. The purpose of these constructs is to dynamically access a property on the fly, usually for some sort of meta-programming, or to arbitrarily pass attributes on to an html element, which is common in UI libraries. The existence of either of them implies arbitrary access to a component that may not have been declared.

There’s one other common use of $$props, and that’s to access props declared as a reserved word. class is a common example of this. For example:

const className = $$props.class;

…since:

export let class = "";

…is not valid. class is a reserved word in JavaScript but there’s a workaround in this specific case. The following is also a valid way to declare that same prop—thanks to Rich Harris for helping with this.

let className;export { className as class };

If your only use of $$props is to access a prop whose name is reserved, you can use this alternative, and maintain better type checking for your component.

Parting thoughts

Svelte is one of the most promising, productive, and frankly fun JavaScript frameworks I’ve worked with. The relative ease with which TypeScript can be added is like a cherry on top. Having TypeScript catch errors early for you can be a real productivity boost. Hopefully this post was of some help achieving that.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir