Sapper with Typescript
Tags: sapper, svelte, typescript
Published: 2020-08-13 19:34:15

So I've decided to use Typescript in my Sapper/Svelte site. It took quite some work and some digging but I finally made it work, and in this post I'm gonna share with you how I did it.

# Enabling Typescript

I used [this repo](https://github.com/babichjacob/sapper-typescript-graphql-template) as a base for the project's configs. I copied `rollup.config.js`, `svelte.config.js`, `tsconfig.json` and the `types` folder.

After all that I added `lang="ts"` to all script tags in my `.svelte` components and fixed the compilation errors. All `.js` files were also converted to `.ts` and the errors were fixed. You could copy `/src/client.ts`, `/src/server.ts`, and `/src/service-worker.ts` from the repo, but I decided to just convert my own code to typescript. Also, these files in the repo add GraphQL support to your server, so in case you're like me and don't need GraphQL it might be easier and quicker to just convert the `.js` files yourself.

**Note:** Also don't forget to install all missing packages with npm.

# Third-party component types

Some svelte components already have type definitions, but in case they don't here's how to create the definitions.

In my case it was the `mdi-svelte` component. I created the file `/src/types/mdi-svelte/index.d.ts` and put the following code in it:

```ts
declare module 'mdi-svelte'
{
    export default class Icon
    {
        $$prop_def: {
            path?: string,
            color?: string,
            size?: string,
        };
    }
}
```

The `$$prop_def` field is used for type-checking. You assign an object to it that contains some or all prop names (or none) along with their type annotations. They need to be optional (have the `?` at the end of their name) or else you will be required to define the props that aren't optional every time you use the component in your app.

Now I can use the component like this in my `.svelte` components:

```html
<script lang="ts">
import Icon from 'mdi-svelte';
</script>

<Icon path="iconPath" color="white"></Icon>
```

If you want to support events and slots you just need to define the `$$events_def` and `$$slot_def` fields. I haven't tried that yet but I assume it works the same as `$$prop_def`.

**Note:** I figured out all this by looking at `node_modules/svelte2tsx/svelte-shims.d.ts`, take a look at it yourself to see how it works.

# The `preload` function

To use Sapper's preload function you need to create another Typescript type definition file. I created `types/index.d.ts` but I think you can name it whatever you want, I don't know a lot about Typescript, sorry :/.

Then you add the following code to the file:
```ts
declare function SapperPreload(
    this: {fetch: (input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>},
    page: {host: string, path: string, params: any, query: any},
    session: any,
): any;
```

Done! Now you can export `preload` from your components like this:

```html
<script context="module" lang="ts">

export const preload: typeof SapperPreload = async function ({ host, pathg, params, query }, session)
{
    //Write some code
}

</script>
```

# Some caveats

I couldn't figure out a way to make Typescript work in inline code in HTML (idk if this is the correct name for it, take a look at the code below and you'll understand it):

```html
<Button hidden={post.owner == ($userId ?? false)}>Edit</Button>
```

There is no `??` operator in JavaScript and, even with Typescript enabled, Sapper complains about this. You also cannot use Typescript's `!` and `?` operators. Probably no type annotations too.

To work around this I just made a variable inside my `<script>` tag

```html
<script lang="ts">
// ...

let postEditble: boolean;
$: postEditable = post.owner == ($userId ?? false);

// ...
</script>
```

and then just use `postEditable` in the HTML. Not the best solution, but it works.

**Note:** to use Typescript type annotations with reactive variables you have to declare them in one line and then make them reactive in another line.

I think this might be caused due to Sapper running before Typescript but I have no idea how to use it (I don't know how to use/understand Rollup properly yet). If you have a solution please let me know in the comments and I'll update the post!

# Extra tips!

#### Slow compilation

If your project is taking upwards of 10~20s to compile and you've been working on it for a long time, try restarting the dev server.

#### Type definition not found

If the TS language server in VS Code complains that a type definition for some imported type doesn't exist, try restarting VS Code or go into a `.ts` file, press F1 and run `Typescript: Restart TS Server`.

Hope I helped you in some way, thanks for reading!