
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 excelente historia de reactividad que es simple, flexible y eficaz; y como marco compilado con anticipación (AOT), tiene un rendimiento y tamaños de paquete increíblemente impresionantes. Esta publicación se centrará en la configuración de TypeScript dentro de las plantillas de Svelte. Si es nuevo en Svelte, le recomiendo que consulte el tutorial introductorio y los documentos.
Si desea seguir el código (o si desea depurar lo que podría faltar en su propio proyecto), puede clonar el repositorio. Tengo sucursales configuradas para demostrar las diversas piezas que revisaré.
Nota: Si bien vamos a integrar manualmente Svelte y Typescript, podría considerar usar la plantilla oficial de Svelte que hace lo mismo si está comenzando un proyecto nuevo. De cualquier manera, esta publicación cubre una configuración de TypeScript que aún es relevante, incluso si usa la plantilla.
Configuración básica de TypeScript y Svelte
Veamos una configuración de línea de base. Si vas al initial-setup
branch en el repositorio, hay un proyecto Svelte simple configurado, con TypeScript. Para ser claros, TypeScript solo funciona de forma independiente .ts
archivos. No está integrado de ninguna manera en Svelte. Lograr la integración de TypeScript es el propósito de esta publicación.
Revisaré algunas piezas que hacen que Svelte y TypeScript funcionen, principalmente porque las cambiaré en un momento, para agregar compatibilidad con TypeScript a las plantillas Svelte.
Primero, tengo un tsconfig.json
Archivo:
{
"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 un node_modules
de la compilación.
Entonces, en typings/index.d.ts
Tengo esto:
declare module "*.svelte" {
const value: any;
export default value;
}
Esto permite que TypeScript coexista con Svelte. Sin esto, TypeScript generaría errores cada vez que se carga un archivo Svelte con una declaración de importación. Por último, necesitamos decirle 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 ejecute npm start
en uno, que iniciará un reloj de paquete web, y npm run tscw
en el otro, para iniciar una tarea de observación de TypeScript. Con suerte, ambos se ejecutarán sin errores. Para verificar realmente que se está ejecutando la verificación de TypeScript, puede cambiar:
let x: number = 12;
…en index.ts
a:
let x: number = "12";
… y vea aparecer el error en el reloj de TypeScript. Si realmente desea ejecutar esto, puede ejecutar node server
en una tercera terminal (recomiendo iTerm2, que le permite ejecutar estas terminales dentro de pestañas en la misma ventana) y luego presione localhost:3001
.
Agregar TypeScript a Svelte
Agreguemos TypeScript directamente a nuestro componente Svelte, luego veamos qué cambios de configuración necesitamos para que funcione. Primero ve a Helper.svelte
, y añadir lang="ts"
a la etiqueta de secuencia de comandos. Eso le dice a Svelte que hay TypeScript dentro del script. Ahora agreguemos algo de TypeScript. Cambiemos el val
prop que se verifica como un número, a través de export let val: number;
. El componente completo ahora se ve así:
<script lang="ts">
export let val: number;
</script>
<h1>Value is: {val}</h1>
Nuestra ventana de 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 cuenta de cheques
Hasta ahora, lo que tenemos se construye, pero no funciona. Si tenemos un código inválido en un componente de Svelte, queremos que genere un error. Entonces, vayamos a App.svelte
, agrega lo mismo lang="ts"
a la etiqueta de secuencia de comandos y luego pasar un valor no válido para la val
prop, así:
<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 nuestro reloj TypeScript y, en esa terminal, ejecutemos npm run svelte-check
. Eso iniciará el proceso de verificación esbelta en modo reloj, y deberíamos ver el error que esperábamos.
Ahora, elimine las comillas alrededor del 3
, y el error debería desaparecer:
¡Limpio!
En la práctica, querríamos que tanto svelte-check como tsc se ejecutaran al mismo tiempo, por lo que detectamos ambos errores en nuestros archivos TypeScript y plantillas Svelte. Hay un montón de utilidades en npm que permiten hacer esto, o podemos usar iTerm2, es capaz de 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 el basic-checking
rama del repositorio.
Atrapando accesorios faltantes
Todavía hay un problema que debemos resolver. Si omitimos un accesorio obligatorio, como el val
prop que acabamos de ver, todavía no obtendremos un error, pero deberíamos, ya que no le asignamos un valor predeterminado en Helper.svelte
, y por lo tanto es obligatorio.
<Helper /> // missing `val` prop
Para decirle a TypeScript que informe esto como un error, volvamos a nuestro tsconfig
y agregue dos nuevos valores
"strict": true,
"noImplicitAny": false
El primero habilita un montón 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 se escribe implícitamente como any
– ahora se informa como un error (no implícito, ¿entendido?)
Las opiniones difieren ampliamente de si noImplicitAny
debe establecerse en true
. Creo que es demasiado estricto, pero mucha gente no está de acuerdo. Experimente y llegue a su propia conclusión.
De todos modos, con esa nueva configuración en su lugar, deberíamos poder reiniciar nuestra tarea de verificación svelte y ver el error que estábamos esperando.
Esta configuración está en el better-checking
rama del repositorio.
Retazos
Una cosa a tener en cuenta es que el mecanismo de TypeScript para detectar propiedades incorrectas se apaga de forma inmediata e irreversible para un componente si ese componente alguna vez hace referencia $$props
o $$restProps
. Por ejemplo, si pasara un accesorio no declarado de, digamos, junk
en el componente Helper, obtendría un error, como se esperaba, ya que ese componente no tiene junk
propiedad. Pero este error desaparecería inmediatamente si el Helper
componente referenciado $$props
o $$restProps
. El primero le permite acceder dinámicamente a cualquier accesorio sin tener una declaración explícita, mientras que $$restProps
es para acceder dinámicamente a accesorios no declarados.
Esto tiene sentido cuando lo piensas. El propósito de estas construcciones es acceder dinámicamente a una propiedad sobre la marcha, generalmente para algún tipo de metaprogramación, o pasar atributos arbitrariamente a un elemento html, que es común en las bibliotecas de UI. La existencia de cualquiera de ellos implica el acceso arbitrario a un componente que puede no haber sido declarado.
Hay otro uso común de $$props
, y eso es para acceder a los accesorios declarados como palabra reservada. class
es un ejemplo común de esto. Por ejemplo:
const className = $$props.class;
…ya que:
export let class = "";
…no es válido. class
es una palabra reservada en JavaScript, pero hay una solución en este caso específico. La siguiente es también una forma válida de declarar ese mismo apoyo: gracias a Rich Harris por ayudar con esto.
let className;
export { className as class };
Si su único uso de $$props
es acceder a un accesorio cuyo nombre está reservado, puede usar esta alternativa y mantener una mejor verificación de tipos para su componente.
Pensamientos de despedida
Svelte es uno de los frameworks JavaScript más prometedores, productivos y francamente divertidos con los que he trabajado. La relativa facilidad con la que se puede agregar TypeScript es como una cereza en la parte superior. Hacer que TypeScript detecte los errores temprano puede ser un verdadero impulso de productividad. Espero que esta publicación haya sido de alguna ayuda para lograrlo.