Skip to content

Commit 3ca86f8

Browse files
committed
typescript
related: sveltejs/svelte#11502
1 parent e19136a commit 3ca86f8

File tree

2 files changed

+234
-2
lines changed

2 files changed

+234
-2
lines changed

apps/svelte.dev/content/docs/svelte/03-runes/02-side-effects.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ You might be tempted to do something convoluted with effects to link one value t
173173
174174
<label>
175175
<input type="range" bind:value={left} max={total} />
176-
{left.value}/{total} left
176+
{left}/{total} left
177177
</label>
178178
```
179179

@@ -310,7 +310,7 @@ import { tick } from 'svelte';
310310

311311
export default function readable<T>(
312312
initial_value: T,
313-
start: (callback: (value: T) => void) => () => void
313+
start: (callback: (update: (v: T) => T) => T) => () => void
314314
) {
315315
let value = $state(initial_value);
316316

apps/svelte.dev/content/docs/svelte/05-misc/03-typescript.md

+232
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,235 @@ title: TypeScript
77
- generics
88
- using `Component` and the other helper types
99
- using `svelte-check`
10+
11+
You can use TypeScript within Svelte components. IDE extensions like the [Svelte VSCode extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) will help you catch errors right in your editor, and [`svelte-check`](https://www.npmjs.com/package/svelte-check) does the same on the command line, which you can integrate into your CI.
12+
13+
## `<script lang="ts">`
14+
15+
To use TypeScript inside your Svelte components, add `lang="ts"` to your `script` tags:
16+
17+
```svelte
18+
<script lang="ts">
19+
let name: string = 'world';
20+
21+
function greet(name: string) {
22+
alert(`Hello, ${name}!`);
23+
}
24+
</script>
25+
26+
<button onclick={(e: Event) => greet(e.target.innerText)}>
27+
{name as string}
28+
</button>
29+
```
30+
31+
Doing so allows you to use TypeScript's _type-only_ features. That is, all features that just disappear when transpiling to JavaScript, such as type annotations or interface declarations. Features that require the TypeScript compiler to output actual code are not supported. This includes:
32+
33+
- using enums
34+
- using `private`, `protected` or `public` modifiers in constructor functions together with initializers
35+
- using features that are not yet part of the ECMAScript standard (i.e. not level 4 in the TC39 process) and therefore not implemented yet within Acorn, the parser we use for parsing JavaScript
36+
37+
If you want to use one of these features, you need to setup up a `script` preprocessor.
38+
39+
## Preprocessor setup
40+
41+
To use non-type-only TypeScript features within Svelte components, you need to add a preprocessor that will turn TypeScript into JavaScript.
42+
43+
### Using SvelteKit or Vite
44+
45+
The easiest way to get started is scaffolding a new SvelteKit project by typing `npm create svelte@latest`, following the prompts and choosing the TypeScript option.
46+
47+
```ts
48+
/// file: svelte.config.js
49+
// @noErrors
50+
import { vitePreprocess } from '@sveltejs/kit/vite';
51+
52+
const config = {
53+
preprocess: vitePreprocess()
54+
};
55+
56+
export default config;
57+
```
58+
59+
If you don't need or want all the features SvelteKit has to offer, you can scaffold a Svelte-flavoured Vite project instead by typing `npm create vite@latest` and selecting the `svelte-ts` option.
60+
61+
```ts
62+
/// file: svelte.config.js
63+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
64+
65+
const config = {
66+
preprocess: vitePreprocess()
67+
};
68+
69+
export default config;
70+
```
71+
72+
In both cases, a `svelte.config.js` with `vitePreprocess` will be added. Vite/SvelteKit will read from this config file.
73+
74+
### Other build tools
75+
76+
If you're using tools like Rollup or Webpack instead, install their respective Svelte plugins. For Rollup that's [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) and for Webpack that's [svelte-loader](https://github.com/sveltejs/svelte-loader). For both, you need to install `typescript` and `svelte-preprocess` and add the preprocessor to the plugin config (see the respective READMEs for more info). If you're starting a new project, you can also use the [rollup](https://github.com/sveltejs/template) or [webpack](https://github.com/sveltejs/template-webpack) template to scaffold the setup from a script.
77+
78+
> If you're starting a new project, we recommend using SvelteKit or Vite instead
79+
80+
## Typing `$props`
81+
82+
Type `$props` just like a regular object with certain properties.
83+
84+
```svelte
85+
<script lang="ts">
86+
import type { Snippet } from 'svelte';
87+
88+
interface Props {
89+
requiredProperty: number;
90+
optionalProperty?: boolean;
91+
snippetWithStringArgument: Snippet<[string]>;
92+
eventHandler: (arg: string) => void;
93+
[key: string]: unknown;
94+
}
95+
96+
let {
97+
requiredProperty,
98+
optionalProperty,
99+
snippetWithStringArgument,
100+
eventHandler,
101+
...everythingElse
102+
}: Props = $props();
103+
</script>
104+
105+
<button onclick={() => eventHandler('clicked button')}>
106+
{@render snippetWithStringArgument('hello')}
107+
</button>
108+
```
109+
110+
## Generic `$props`
111+
112+
Components can declare a generic relationship between their properties. One example is a generic list component that receives a list of items and a callback property that reveives an item from the list. To declare that the `items` property and the `select` callback operate on the same types, add the `generics` attribute to the `script` tag:
113+
114+
```svelte
115+
<script lang="ts" generics="Item extends { text: string }">
116+
interface Props {
117+
items: Item[];
118+
select: Item;
119+
}
120+
121+
let { items, select } = $props();
122+
</script>
123+
124+
{#each items as item}
125+
<button onclick={() => select(item)}>
126+
{item.text}
127+
</button>
128+
{/each}
129+
```
130+
131+
The content of `generics` is what you would put between the `<...>` tags of a generic function. In other words, you can use multiple generics, `extends` and fallback types.
132+
133+
## Typing `$state`
134+
135+
You can type `$state` like any other variable.
136+
137+
```ts
138+
let count: number = $state(0);
139+
```
140+
141+
If you don't give `$state` an initial value, part of its types will be `undefined`.
142+
143+
```ts
144+
// @noErrors
145+
// Error: Type 'number | undefined' is not assignable to type 'number'
146+
let count: number = $state();
147+
```
148+
149+
If you know that the variable _will_ be defined before you first use it, use an `as` casting. This is especially useful in the context of classes:
150+
151+
```ts
152+
class Counter {
153+
count = $state() as number;
154+
constructor(initial: number) {
155+
this.count = initial;
156+
}
157+
}
158+
```
159+
160+
## The `Component` type
161+
162+
Svelte components or of type `Component`. You can use it and its related types to express a variety of constraints.
163+
164+
Using it together with `<svelte:component>` to restrict what kinds of component can be passed to it:
165+
166+
```svelte
167+
<script lang="ts">
168+
import type { Component } from 'svelte';
169+
170+
interface Props {
171+
// only components that have at most the "prop"
172+
// property required can be passed
173+
component: Component<{ prop: string }>
174+
}
175+
176+
let { component }: Props = $props();
177+
</script>
178+
179+
<svelte:component this={component} prop="foo" />
180+
```
181+
182+
Closely related to the `Component` type is the `ComponentProps` type which extracts the properties a component expects.
183+
184+
```ts
185+
import type { Component, ComponentProps } from 'svelte';
186+
import MyComponent from './MyComponent.svelte';
187+
188+
function withProps<TComponent extends Component<any>>(
189+
component: TComponent,
190+
props: ComponentProps<TComponent>
191+
) {}
192+
193+
// Errors if the second argument is not the correct props expected
194+
// by the component in the first argument.
195+
withProps(MyComponent, { foo: 'bar' });
196+
```
197+
198+
## Enhancing built-in DOM types
199+
200+
Svelte provides a best effort of all the HTML DOM types that exist. Sometimes you may want to use experimental attributes or custom events coming from an action. In these cases, TypeScript will throw a type error, saying that it does not know these types. If it's a non-experimental standard attribute/event, this may very well be a missing typing from our [HTML typings](https://github.com/sveltejs/svelte/blob/main/packages/svelte/elements.d.ts). In that case, you are welcome to open an issue and/or a PR fixing it.
201+
202+
In case this is a custom or experimental attribute/event, you can enhance the typings like this:
203+
204+
```ts
205+
/// file: additional-svelte-typings.d.ts
206+
declare namespace svelteHTML {
207+
// enhance elements
208+
interface IntrinsicElements {
209+
'my-custom-element': { someattribute: string; 'on:event': (e: CustomEvent<any>) => void };
210+
}
211+
// enhance attributes
212+
interface HTMLAttributes<T> {
213+
// If you want to use on:beforeinstallprompt
214+
'on:beforeinstallprompt'?: (event: any) => any;
215+
// If you want to use myCustomAttribute={..} (note: all lowercase)
216+
mycustomattribute?: any; // You can replace any with something more specific if you like
217+
}
218+
}
219+
```
220+
221+
Then make sure that `d.ts` file is referenced in your `tsconfig.json`. If it reads something like `"include": ["src/**/*"]` and your `d.ts` file is inside `src`, it should work. You may need to reload for the changes to take effect.
222+
223+
You can also declare the typings by augmenting the `svelte/elements` module like this:
224+
225+
```ts
226+
/// file: additional-svelte-typings.d.ts
227+
import { HTMLButtonAttributes } from 'svelte/elements';
228+
229+
declare module 'svelte/elements' {
230+
export interface SvelteHTMLElements {
231+
'custom-button': HTMLButtonAttributes;
232+
}
233+
234+
// allows for more granular control over what element to add the typings to
235+
export interface HTMLButtonAttributes {
236+
veryexperimentalattribute?: string;
237+
}
238+
}
239+
240+
export {}; // ensure this is not an ambient module, else types will be overridden instead of augmented
241+
```

0 commit comments

Comments
 (0)