Skip to main content

Outros

TypeScript

Editar esta página na GitHub

Nós podemos usar a TypeScript dentro dos componentes da Svelte. Extensões de ambiente de desenvolvimento integrado como a extensão de VSCode da Svelte ajudar-nos-á a capturar os erros diretamente no nosso editor, e o svelte-check faz o mesmo na linha de comando, o qual podemos integrar na nossa integração continua.

Configuração

Para usarmos a TypeScript dentro dos componentes da Svelte, precisamos adicionar um pré-processador que transformará a TypeScript em JavaScript.

Usando a SvelteKit ou a Vite

A maneira mais fácil de começar é estruturando um novo projeto de SvelteKit digitando npm create svelte@latest, seguindo os prontos e escolhendo a opção TypeScript:

svelte.config.js
ts
import { vitePreprocess } from '@sveltejs/kit/vite';
 
const config = {
preprocess: vitePreprocess()
};
 
export default config;

Se não precisamos ou queremos todas as funcionalidades que a SvelteKit tem a oferecer, podemos estruturar um projeto de Vite temperado com a Svelte digitando npm create vite@latest e selecionando a opção svelte-ts:

svelte.config.js
ts
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
 
const config = {
preprocess: vitePreprocess()
};
 
export default config;

Em ambos casos, um svelte.config.js com vitePreprocess será adicionado. A Vite ou a SvelteKit lerá a partir deste ficheiro de configuração.

Outras Ferramentas de Construção

Se estivermos usando ferramentas como a Rollup ou a Webpack, instalamos as suas respetivas extensões de Svelte. Para a Rollup é rollup-plugin-svelte e para a Webpack é svelte-loader. Para ambas, precisamos instalar a typescript e a svelte-preprocess e adicionar o pré-processador à configuração da extensão (consulte as respetivas READMEs por mais informação). Se estivermos começando um novo projeto, também podemos usar o modelo de projeto da rollup ou da webpack para estruturar a configuração a partir dum programa.

Se estivermos a começar um novo projeto, recomendamos usar a SvelteKit ou Vite

<script lang="ts">

Para usarmos a TypeScript dentro dos nossos componentes da Svelte, adicionamos lang="ts" aos nossos marcadores de script:

<script lang="ts">
  let name: string = 'world';

  function greet(name: string) {
    alert(`Hello, ${name}!`);
  }
</script>

Propriedades

As propriedades podem ser tipificadas diretamente sobre a declaração export let:

<script lang="ts">
  export let name: string;
</script>

Ranhuras

A ranhura e os tipos da propriedades do slot são inferidos a partir dos tipos das propriedades da ranhura passadas às mesmas:

<script lang="ts">
  export let name: string;
</script>

<slot {name} />

<!-- Depois -->
<Comp let:name>
  <!--    ^ Inferido como sequência de caracteres -->
  {name}
</Comp>

Eventos

Os eventos podem ser tipificados com createEventDispatcher:

<script lang="ts">
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher<{
    event: null; // não aceita uma carga
    click: string; // tem uma carga de sequência de caracteres obrigatória
    type: string | null; // tem uma carga de sequência de caracteres opcional
  }>();

  function handleClick() {
    dispatch('event');
    dispatch('click', 'hello');
  }

  function handleType() {
    dispatch('event');
    dispatch('type', Math.random() > 0.5 ? 'world' : null);
  }
</script>

<button on:click={handleClick} on:keydown={handleType}>Click</button>

Melhorando os Tipos do DOM Embutidos

A Svelte fornece um melhor esforço para todos os tipos do DOM de HTML que existem. Algumas vezes podemos desejar usar atributos experimentais ou eventos personalizados vindos a partir duma ação. Se for um atributo ou evento padrão não experimental, este pode muito bem uma tipificação em falta da nossas tipificações de HTML. Neste caso, todos são bem-vindos a abrir uma questão ou um pedido de atualização do repositório para correção.

No caso que for um evento ou atributo experimental ou personalizado, podemos melhor as tipificações com isto:

additional-svelte-typings.d.ts
ts
declare namespace svelteHTML {
// melhorar os elementos
interface IntrinsicElements {
'my-custom-element': { someattribute: string; 'on:event': (e: CustomEvent<any>) => void };
}
// melhorar os atributos
interface HTMLAttributes<T> {
// Se quisermos usar `on:beforeinstallprompt`
'on:beforeinstallprompt'?: (event: any) => any;
// Se quisermos usar `myCustomAttribute={..}` (nota: tudo minúsculo)
mycustomattribute?: any; // Podemos substituir `any` por algo mais específico se quisermos
}
}

Depois certificamos-nos de que o ficheiro d.ts é referenciando no nosso tsconfig.json. Se este precisar de algo como "include": ["src/**/*"] e o nosso ficheiro d.ts estiver dentro de src, deve funcionar. Nós podemos precisar recarregar para as mudanças surtirem efeito.

Desde a versão 4.2 da Svelte ou versão 3.5 do svelte-check ou versão 107.10.0 da extensão do VSCode também podemos declarar as tipificações aumentando o módulo svelte/elements desta maneira:

additional-svelte-typings.d.ts
ts
import { HTMLButtonAttributes } from 'svelte/elements';
 
declare module 'svelte/elements' {
export interface SvelteHTMLElements {
'custom-button': HTMLButtonAttributes;
}
 
// permite controle mais granular sobre para qual elemento adicionar as tipificações
export interface HTMLButtonAttributes {
veryexperimentalattribute?: string;
}
}
 
export {}; // garantir que isto não é um módulo ambiente, senão os tipos serão sobrepostos ao invés de aumentados

Tipificações Avançadas Experimentais

Algumas funcionalidades não estão tirando total vantagem da TypeScript nos casos de uso mais avançados como tipificar que um componente implementa uma certa interface, explicitamente tipificando as ranhuras, ou usando os genéricos. Estas coisas são possíveis usando as capacidades de tipo avançado experimentais. Consulte esta RFC por mais informação sobre como fazer uso das mesmas.

A API é experimental e pode mudar em algum ponto.

Limitações

Sem TypeScript na Marcação

Nós não podemos usar TypeScript na marcação do nosso modelo de marcação de hipertexto. Por exemplo, o seguinte não funciona:

<script lang="ts">
  let count = 10;
</script>

<h1>Count as string: {count as string}!</h1> <!-- ❌ Não funciona -->
{#if count > 4}
  {@const countString: string = count} <!-- ❌ Não funciona -->
  {countString}
{/if}

Declarações Reativas

Nós não podemos tipificar as nossas declarações reativas com a TypeScript da maneira que tipificamos uma variável. Por exemplo, o seguinte não funciona:

<script lang="ts">
  let count = 0;

  $: doubled: number = count * 2; // ❌ Não funciona
</script>

Não podemos adicionar um : TYPE porque é sintaxe inválida nesta posição. No lugar disto, podemos mover a definição para uma declaração de let como acima:

<script lang="ts">
  let count = 0;

  let doubled: number;
  $: doubled = count * 2;
</script>

Tipos

ComponentConstructorOptions

Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. Use mount or createRoot instead to instantiate components. See breaking changes for more info.

ts
interface ComponentConstructorOptions<
Props extends Record<string, any> = Record<string, any>
> {}
ts
target: Element | Document | ShadowRoot;
ts
anchor?: Element;
ts
props?: Props;
ts
context?: Map<any, any>;
ts
hydrate?: boolean;
ts
intro?: boolean;
ts
$$inline?: boolean;

ComponentEvents

Convenience type to get the events the given component expects. Example:

<script lang="ts">
   import type { ComponentEvents } from 'svelte';
   import Component from './Component.svelte';

   function handleCloseEvent(event: ComponentEvents<Component>['close']) {
    console.log(event.detail);
   }
</script>

<Component on:close={handleCloseEvent} />
ts
type ComponentEvents<Comp extends SvelteComponent> =
Comp extends SvelteComponent<any, infer Events>
? Events
: never;

ComponentProps

Convenience type to get the props the given component expects. Example:

<script lang="ts">
  import type { ComponentProps } from 'svelte';
  import Component from './Component.svelte';

  const props: ComponentProps<Component> = { foo: 'bar' }; // Errors if these aren't the correct props
</script>
ts
type ComponentProps<Comp extends SvelteComponent> =
Comp extends SvelteComponent<infer Props> ? Props : never;

ComponentType

Convenience type to get the type of a Svelte component. Useful for example in combination with dynamic components using <svelte:component>.

Example:

<script lang="ts">
  import type { ComponentType, SvelteComponent } from 'svelte';
  import Component1 from './Component1.svelte';
  import Component2 from './Component2.svelte';

  const component: ComponentType = someLogic() ? Component1 : Component2;
  const componentOfCertainSubType: ComponentType<SvelteComponent<{ needsThisProp: string }>> = someLogic() ? Component1 : Component2;
</script>

<svelte:component this={component} />
<svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
ts
type ComponentType<
Comp extends SvelteComponent = SvelteComponent
> = (new (
options: ComponentConstructorOptions<
Comp extends SvelteComponent<infer Props>
? Props
: Record<string, any>
>
) => Comp) & {
/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */
element?: typeof HTMLElement;
};

EventDispatcher

ts
interface EventDispatcher<
EventMap extends Record<string, any>
> {}
ts
<Type extends keyof EventMap>(
...args: null extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: undefined extends EventMap[Type]
? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]
: [type: Type, parameter: EventMap[Type], options?: DispatchOptions]
): boolean;

Snippet

The type of a #snippet block. You can use it to (for example) express that your component expects a snippet of a certain type:

ts
let { banner } = $props<{ banner: Snippet<{ text: string }> }>();

You can only call a snippet through the {@render ...} tag.

ts
interface Snippet<T = void> {}
ts
(arg: T): typeof SnippetReturn & {
_: 'functions passed to {@render ...} tags must use the `Snippet` type imported from "svelte"';
};

SvelteComponent

Can be used to create strongly typed Svelte components.

Example:

You have component library on npm called component-library, from which you export a component called MyComponent. For Svelte+TypeScript users, you want to provide typings. Therefore you create a index.d.ts:

ts
import { SvelteComponent } from "svelte";
export class MyComponent extends SvelteComponent<{foo: string}> {}

Typing this makes it possible for IDEs like VS Code with the Svelte extension to provide intellisense and to use the component like this in a Svelte file with TypeScript:

<script lang="ts">
  import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />

This was the base class for Svelte components in Svelte 4. Svelte 5+ components are completely different under the hood. You should only use this type for typing, not actually instantiate components with new - use mount or createRoot instead. See breaking changes for more info.

ts
class SvelteComponent<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> {}
ts
[prop: string]: any;
ts
constructor(options: ComponentConstructorOptions<PropsWithChildren<Props, Slots>>);
ts
$destroy(): void;
ts
$on<K extends Extract<keyof Events, string>>(
type: K,
callback: (e: Events[K]) => void
): () => void;
ts
$set(props: Partial<Props>): void;

SvelteComponentTyped

Use SvelteComponent instead. See TODO for more information.

ts
class SvelteComponentTyped<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> extends SvelteComponent<Props, Events, Slots> {}