{"id":13599004,"url":"https://github.com/ivanhofer/sveltekit-typescript-showcase","last_synced_at":"2025-04-04T08:09:45.213Z","repository":{"id":37240627,"uuid":"467173851","full_name":"ivanhofer/sveltekit-typescript-showcase","owner":"ivanhofer","description":"This repository shows how Svelte and SvelteKit work together with TypeScript.","archived":false,"fork":false,"pushed_at":"2023-07-10T11:44:30.000Z","size":390,"stargazers_count":735,"open_issues_count":0,"forks_count":36,"subscribers_count":25,"default_branch":"main","last_synced_at":"2024-10-14T08:46:13.174Z","etag":null,"topics":["showcase","svelte","sveltekit","tipps","typescript"],"latest_commit_sha":null,"homepage":"","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivanhofer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":["ivanhofer"]}},"created_at":"2022-03-07T16:31:26.000Z","updated_at":"2024-10-07T05:05:27.000Z","dependencies_parsed_at":"2024-04-09T23:48:34.565Z","dependency_job_id":null,"html_url":"https://github.com/ivanhofer/sveltekit-typescript-showcase","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Fsveltekit-typescript-showcase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Fsveltekit-typescript-showcase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Fsveltekit-typescript-showcase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Fsveltekit-typescript-showcase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivanhofer","download_url":"https://codeload.github.com/ivanhofer/sveltekit-typescript-showcase/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142074,"owners_count":20890653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["showcase","svelte","sveltekit","tipps","typescript"],"created_at":"2024-08-01T17:00:58.851Z","updated_at":"2025-04-04T08:09:45.188Z","avatar_url":"https://github.com/ivanhofer.png","language":"Svelte","funding_links":["https://github.com/sponsors/ivanhofer"],"categories":["Resources","Sites","🏃 SvelteKit Starter Kits and Integrations"],"sub_categories":["Tutorials","The _How To's?_"],"readme":"# Svelte(Kit) TypeScript Showcase + general TypeScript tipps\r\n\r\nThis repository shows how [`Svelte`](https://svelte.dev/) and [`SvelteKit`](https://kit.svelte.dev/)\r\nwork together with [`TypeScript`](https://www.typescriptlang.org/).\r\n\r\n\u003e This repository should offer an overview of all `TypeScript` related topics for `Svelte` and\r\n\u003e `SvelteKit`\\\r\n\u003e Feel free to contribute by [creating a PR](https://github.com/ivanhofer/sveltekit-typescript-showcase/pulls)\r\n\u003e or [open an issue](https://github.com/ivanhofer/sveltekit-typescript-showcase/issues) if you think\r\n\u003e something is missing.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Who am I\r\n\r\nHi, I'm Ivan, a passionate webdeveloper.\r\n\r\nI recently have been working more intensively with `TypeScript` when I have created an\r\ninternationalization library focusing on developer experience with strong typesafety features:\r\n[`typesafe-i18n`](https://github.com/ivanhofer/typesafe-i18n)\r\n\r\nI know and love `Svelte` for a few years now. Over the years I saw how my development workflow\r\nimproved and now together with `TypeScript`, I'm able to build real business applications with\r\nconfidence. When I started with `Svelte`, the missing `TypeScript` support always bothered me. And\r\nonce I could use `TypeScript` in my `Svelte` projects I still found it not so easy because of some\r\nmissing documentation. That's why I decided to create this repository with some examples that should\r\nhelp you learn the concepts better. I hope you find it useful.\r\n\r\n[Become a sponsor :heart:](https://github.com/sponsors/ivanhofer) if you want to support my open source contributions.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Overview\r\n\r\n-  [**Project Setup**](#project-setup): good to know before getting started\r\n-  [**Examples**](#examples): see `TypeScript` + `Svelte` in action\r\n-  [**TypeScript Tipps**](#typescript-tipps): tipps to increase the typesafety of your projects\r\n-  [**Conclusion**](#conclusion): some final words\r\n-  [**JSDoc comments**](#jsdoc-comments): benefit from type-checkings without writing `TypeScript`\r\n   code\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Project Setup\r\n\r\nIn order to get the best development expeience you should use\r\n[`VS Code`](https://code.visualstudio.com/) as your IDE and install the\r\n[official `Svelte` extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).\r\nThe extension offers you a variety of features like code-completions, hover infos, syntax error\r\nhighlighting and much more.\r\n\r\n\u003e I also recommend to install the\r\n\u003e [`Error Lens` extension](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens)\r\n\u003e that displays error-messages inline next to the actual code.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### Get Started\r\n\r\nYou can create a new `SvelteKit` project by following the\r\n[`Getting started` guide](https://kit.svelte.dev/docs/introduction#getting-started) in the official\r\ndocs.\r\n\r\n```sh\r\nnpm init svelte my-app\r\ncd my-app\r\nnpm install\r\nnpm run dev\r\n```\r\n\r\nThe `npm init svelte my-app` command starts an interactive project-setup process where you get\r\nasked a few questions. Of course you should select the `TypeScript` option.\r\n\r\n\u003e I also recommend enabling the [`eslint`](https://eslint.org/) and\r\n\u003e [`prettier`](https://prettier.io/) options.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### Configure TypeScript\r\n\r\nIn the root of the generated folder, you should see a\r\n[`tsconfig.json` file](https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/tsconfig.json).\r\nI recommend you to configure `TypeScript` as strict as possible to benefit from the advanced\r\ntype-checking features.\r\n\r\n\u003e As of writing these are the strictest options I know. This list will grow with new `TypeScript`\r\n\u003e releases.\\\r\n\u003e Please [create a PR](https://github.com/ivanhofer/sveltekit-typescript-showcase/pulls) if you know\r\n\u003e more options that should be enabled.\r\n\r\n```jsonc\r\n{\r\n   \"compilerOptions\": {\r\n      \"strict\": true,\r\n      \"allowUnreachableCode\": false,\r\n      \"exactOptionalPropertyTypes\": true,\r\n      \"noImplicitAny\": true,\r\n      \"noImplicitOverride\": true,\r\n      \"noImplicitReturns\": true,\r\n      \"noImplicitThis\": true,\r\n      \"noFallthroughCasesInSwitch\": true,\r\n      \"noUncheckedIndexedAccess\": true\r\n   }\r\n}\r\n```\r\n\r\n\u003e Not all options may fit your coding style. You can always remove some of them but be aware that\r\n\u003e this could eliminate some cool type-checking features.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### How does TypeScript work inside Svelte-Components?\r\n\r\n\u003e Svelte has added a [`TypeScript`-section](https://svelte.dev/docs/typescript) to their official docs.\r\n\r\nPer default the `Svelte`-compiler only understands plain `HTML`, `CSS` and `JavaScript`. But we can\r\nadd support for other languages to the compiler via custom\r\n[`preprocessors`](https://svelte.dev/docs#compile-time-svelte-preprocess). Luckily, we don't have to\r\nwrite our own preprocessor because there exists already an\r\n[official package](https://github.com/sveltejs/svelte-preprocess) we can use. `svelte-preprocess`\r\nenables us to use `TypeScript` and also custom CSS syntax like `SCSS` or `PostCSS` without much\r\neffort. If you take a look at the\r\n[`svelte.config.js` file](https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/svelte.config.js),\r\nyou see that this was arleady set up for us.\r\n\r\nThe next step will be to create our first Component and use `TypeScript` inside the `script`-tag.\r\n\r\n```svelte\r\n\u003cscript\u003e\r\n   export let name: string\r\n\u003c/script\u003e\r\n\r\nHello {name}!\r\n```\r\n\r\nIf everything is correctly set up, you should see an error message telling you something like\r\n`'Unexpected Token'`. That's because we have to tell the preprocessor that we want to use\r\n`TypeScript` syntax. We can do that by adding the `lang=\"ts\"` attribute to our `script` tag.\r\n\r\n```diff\r\n-\u003cscript\u003e\r\n+\u003cscript lang=\"ts\"\u003e\r\n   export let name: string\r\n\u003c/script\u003e\r\n\r\nHello {name}!\r\n```\r\n\r\nThat's it. You are now ready to write `TypeScript` code inside your `Svelte`-components.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### imports from `.ts` files\r\n\r\nYou can also import functions from other `TypeScript` files like you would in a normal `.ts` file.\r\nIf you import types from another file, make sure you use the\r\n[`import type`-syntax](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)\r\n\r\n```svelte\r\n\u003cscript\u003e\r\n   import { myFunction } from './my-file'\r\n   import type { MyFunctionType } from './my-file'\r\n\u003c/script\u003e\r\n```\r\n\r\nif you are using a `TypeScript` version `\u003e= 4.5.x` you can also write it like this:\r\n\r\n```svelte\r\n\u003cscript\u003e\r\n   import { myFunction, type MyFunctionType } from './my-file'\r\n\u003c/script\u003e\r\n```\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### type-checking the whole project\r\n\r\nYou should know that even if you have `TypeScript` errors in your code, the `Svelte`-compiler will\r\ngenerate your component (if the code contains valid `TypeScript` syntax) and the browser will run\r\nthe code normally. That's because the `preprocessor` only transpiles `TypeScript` to `JavaScript`\r\nand doesn't perform any type-checking. That's a reason why we should use the `Svelte` extension that\r\nwill perform the type-checking for the components we have opened in `VS Code`. Performing a global\r\ntype-check for all components each time you save a file may be too resource intensive for most\r\ncomputers, so only errors for opened files will show up.\r\n\r\nThis approach has a downside: If we change something in a component and haven't opened the file\r\nwhere we use that specific component, we won't get notified about errors. Again luckily for us there\r\nexists a solution to this problem: a package called\r\n[svelte-check](https://github.com/sveltejs/language-tools/tree/master/packages/svelte-check).\r\n\r\nIf you take a look at the `scripts` section of the `package.json`, you will see that it is already\r\nconfigured for us. We simply can run the following command to perform a check of the whole project:\r\n\r\n```sh\r\nnpm run check\r\n```\r\n\r\n\u003e You should include this `svelte-check` script in your `CI/CD` process to get notified if your\r\n\u003e components contain `TypeScript` errors.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### path aliases\r\n\r\nYou may see imports from paths that are not npm modules and also not relative paths e.g.\r\n`$components/Component.svelte`. These are called path aliases and you can define them yourself if you like.\r\n\r\nLet's say instead of using a relative file import like `../../../components/Component.svelte` you want to\r\nuse the alias import `$components/Component.svelte`. To do that, you only need to define the desired alias\r\nin your `svelte.config.js` file:\r\n\r\n```ts\r\n/** @type {import('@sveltejs/kit').Config} */\r\nconst config = {\r\n  kit: {\r\n    alias: {\r\n      $components: 'src/components',\r\n    }\r\n  }\r\n};\r\n```\r\n\r\nThese aliases are automatically passed to Vite and TypeScript. You can define as many aliases as you want.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Examples\r\n\r\nThe next sections contain a list of all examples included in this repo. It is recommended to explore\r\nthe examples inside `VS Code` to have proper syntax-highlighting in place.\r\n\r\n-  the title links directly to the folder, where the example is located\r\n-  there is also a short description what is included in the example\r\n-  a link to the official documentation to gain more information (if available)\r\n-  most examples contain a `Component.svelte` and an `Usage.svelte` file to show it in action\r\n\r\n\u003e I recommend going over the examples in this order since some examples build on top of each other.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### Svelte\r\n\r\n#### props\r\n\r\n\u003e https://svelte.dev/docs#component-format-script-1-export-creates-a-component-prop\r\n\r\nThis chapter teaches you everything about how you can use `TypeScript` to improve your component's\r\nprops.\r\n\r\n-  **[basics](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/01-basics)**:\r\n   how to define types for props\r\n-  **[optional props](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/02-optional-props)**:\r\n   how to mark props as optional\r\n-  **[control flow](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/03-control-flow)**:\r\n   how does it work inside the html markup **[(TS-tipp #1)](#1-union-types)**\r\n-  **[reactive assignments](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/04-reactive-assignments)**:\r\n   how to type reactive statements\r\n   \u003e https://svelte.dev/docs#component-format-script-3-$-marks-a-statement-as-reactive\r\n-  **[generic props](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/05-generic-props)**:\r\n   how to use generics for props\r\n-  **[\\$$Props](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/06-$$Props)**:\r\n   how to extend the defined props of your components\r\n-  **[call exported function](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/07-call-exported-function)**:\r\n   how to call an exported function from the parent\r\n-  **[either or](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/08-either-or)**:\r\n   expect either param A or param B\r\n-  **[svelte:component](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/09-svelte-component)**:\r\n   getting prop-types of a component (useful for `svelte:component`)\r\n-  **[prop controls type](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/10-prop-controls-type)**:\r\n   using a prop to control the type of another prop e.g passing single vs multiple items\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### dom\r\n\r\nIn this chapter you will learn how to interact directly with DOM-elements.\r\n\r\n-  **[DOM element bindings](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/02-dom/01-dom-bindings)**:\r\n   how to type your element bindings\r\n   \u003e https://svelte.dev/docs#template-syntax-element-directives-bind-this\r\n-  **[actions](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/02-dom/02-actions)**:\r\n   how to define actions in a typesafe way\r\n   \u003e https://svelte.dev/docs#template-syntax-element-directives-use-action\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### events\r\n\r\n\u003e https://svelte.dev/docs#run-time-svelte-createeventdispatcher\r\n\r\nThis chapter shows how you can define events that a component emits.\r\n\r\n-  **[basics](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/03-events/01-basics)**:\r\n   how to emit events\r\n-  **[typed event details](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/03-events/02-typed-event-details)**:\r\n   how to type the emitted events **[(TS-tipp #2)](#2-extend-existing-type-definitions)**\r\n-  **[generic events](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/03-events/03-generic-events)**:\r\n   how to use generics in your events\r\n-  **[\\$$Events](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/03-events/04-$$Events)**:\r\n   how to extend the defined events of your components\r\n-  **[strictEvents](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/03-events/05-strictEvents)**:\r\n   how to disallow to listen for other events than the ones defined in a component\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### slots\r\n\r\n\u003e https://svelte.dev/docs#template-syntax-slot\r\n\r\n-  **[basics](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/04-slots/01-basics)**:\r\n   how to define slots and expose props\r\n-  **[named](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/04-slots/02-named-slots)**:\r\n   how to define named slots and expose props\r\n   \u003e https://svelte.dev/docs#template-syntax-slot-slot-name-name\r\n-  **[\\$$Slots](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/04-slots/03-$$Slots)**:\r\n   how to extend the defined slots of your components\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### external components\r\n\r\n-  **[SvelteComponent](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/05-external-components)**:\r\n   how to write type definitions for external components\r\n-  **[`svelte-kit package`](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/lib)**:\r\n   how to create a `Svelte` component library\r\n\r\n   \u003e https://kit.svelte.dev/docs/packaging\r\n\r\n   `SvelteKit` includes an easy way to export single components or component libraries. Just run\r\n   `npm run package` and `SvelteKit` will export all your components from the `src/lib` directory,\r\n   together with `TypeScript` definitions into the `package` folder. This folder then also contains\r\n   a generated `package.json` file. After that, you only need to run `npm publish` inside this\r\n   folder to upload the library to `npm`.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### state management\r\n\r\n-  **[stores](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/06-state-management/01-stores)**:\r\n   how to type your stores\r\n   \u003e https://svelte.dev/docs#run-time-svelte-store\r\n-  **context**\r\n   -  **[inline](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/06-state-management/02-context-inline)**:\r\n      how to type contexts\r\n      \u003e https://svelte.dev/docs#run-time-svelte-setcontext\r\n   -  **[outsourced](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/06-state-management/03-context-outsourced)**:\r\n      how to wrap contexts with `TypeScript`\r\n      **[(TS-tipp #3)](#3-wrap-functions-that-have-no-strong-typings)**\r\n\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### other\r\n\r\n-  **[type cast in markup](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/07-other/01-type-cast-in-markup)**:\r\n   how to cast a type within the markup\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n### SvelteKit\r\n\r\nIn this chapter you get to know how to type the backend of your `SvelteKit` application.\r\n\r\n-  **[hooks](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/hooks.server.ts)**:\r\n   how to intercept and modify requests\r\n\r\n   \u003e https://kit.svelte.dev/docs/hooks#server-hooks\r\n\r\n   The `src/hooks.server.ts` file can export four functions. The type of these functions have the same name\r\n   like the function and get exported from `@sveltejs/kit`.\r\n\r\n   ```ts\r\n   import type { HandleFetch, Handle, HandleServerError } from '@sveltejs/kit'\r\n\r\n   export const handle: Handle = async ({ event, resolve }) =\u003e {\r\n      /* implementation */\r\n   }\r\n\r\n   export const handleError: HandleServerError = async ({ error, event }) =\u003e {\r\n      /* implementation */\r\n   }\r\n\r\n   export const handleFetch: HandleFetch = async (request) =\u003e {\r\n      /* implementation */\r\n   }\r\n   ```\r\n\r\n   The `handle` and `getSession` function will have access to the\r\n   [`locals`](https://kit.svelte.dev/docs/hooks#handle) and the\r\n   [`session`](https://kit.svelte.dev/docs/hooks#getsession) object. To let `TypeScript` know how the\r\n   type of these objects look like, you need to go into the `src/app.d.ts` file and update the already\r\n   existing `interfaces` there.\r\n\r\n   Since these types will be shared across multiple files and functions, it makes sense to define them\r\n   just a single time. `SvelteKit` is configured in a way that it automatically uses those types for\r\n   all functions.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n-  **[endpoints](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/routes/products/[id].ts)**:\r\n   how to use `SvelteKit` as an API-endpoint\r\n\r\n   \u003e https://kit.svelte.dev/docs/routing#endpoints\r\n\r\n   We can use `RequestHandler` to type our endpoints. It expects a single generics:\r\n\r\n   1. The type describes the shape the returned value will have.\r\n\r\n   _`src/routes/product/[id].ts`_\r\n\r\n   ```ts\r\n   import type { RequestHandler } from './$types'\r\n   import type { Product } from '$models/product.model'\r\n   import db from '$db'\r\n\r\n   type OutputType = { product: Product }\r\n\r\n   export const GET: RequestHandler\u003cOutputType\u003e = async ({ params }) =\u003e {\r\n      const data = await db.getProjectById(params.id)\r\n\r\n      return {\r\n         body: {\r\n            product: data,\r\n         },\r\n      }\r\n   }\r\n   ```\r\n\r\n   \u003e Note: SvelteKit auto-generates the `./$types` folder for us. We can use it to get a `RequestHandler` type that already has the correct shape for the `params` object.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n-  **[load function](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/routes/products/[id].svelte)**:\r\n   how to load data before the page gets rendered\r\n\r\n   \u003e https://kit.svelte.dev/docs/loading\r\n\r\n   Use the `Load` inferface type load functions in your route. It expects two generics:\r\n\r\n   1. The first type will be the output type of your `endpoint` if available.\\\r\n      If no `GET`-endpoint is defined, the `props` object will be `undefined`.\r\n   2. The second type describes the shape the returned value will have.\r\n\r\n\r\n   _`src/routes/product/[id]/+page.svelte`_\r\n   ```ts\r\n   import type { PageLoad, PageLoadData } from './$types'\r\n   import type { GET } from './[id]'\r\n\r\n   type OutputProps = PageLoadData \u0026 { id: string }\r\n   // the same as\r\n   // type OutputProps = {\r\n   //    id: string\r\n   //    product: Product\r\n   // }\r\n\r\n   export const load: PageLoad\u003cOutputProps\u003e = async ({ params, props }) =\u003e {\r\n      return {\r\n         props: {\r\n            id: params.id,\r\n            product: props.product,\r\n         },\r\n      }\r\n   }\r\n   ```\r\n\r\n   _`src/routes/product/[id]/+page.svelte`_\r\n   ```svelte\r\n   \u003cscript lang=\"ts\"\u003e\r\n      import type { PageData } from './$types'\r\n\r\n      // PageData = { id: string; product: Product }\r\n      export let data: PageData\r\n   \u003c/script\u003e\r\n   ```\r\n\r\n   \u003e Note: SvelteKit auto-generates the `./$types` folder for us. We can use it to get a `Load` type that already has the correct shape for the `params` object.\r\n\r\n\r\n-  **[auto generated types](https://kit.svelte.dev/docs/types#generated-types)**\\\r\n   SvelteKit creates some types automatically. Useful when you want to type your Endpoints and Load functions. Those types contain a typed `params` object depending on the route folder structure you use. The types are generated inside the `./$types` folder.\\\r\n   The types are generated when you run the dev server `npm run dev`. If you just want to generate the types, without running the dev server you can run `npx svelte-kit sync`. When you run `npm install`, the types will be generated automatically because the SvelteKit runs a post-install script that generates the files.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## TypeScript tipps\r\n\r\nHere are some examples how you could improve your code base by adding stronger type definitions for\r\ncommon use cases. Not everything needs to be typed that strong. Stricter type definitions will also\r\nadd complexity you need to maintain, but it certainly improves the devloper experience when using\r\nstrong typed functions within the code base.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### #1 union types\r\n\r\n\u003e [Example](https://github.com/ivanhofer/sveltekit-typescript-showcase/tree/main/src/01-props/03-control-flow/ImprovedComponent.svelte)\r\n\r\nYou can use union types to narrow down types in the control flow of your application. Somewhere you\r\nprobably need to fetch data from an api. The fetch function will probably either return data or an\r\nerror. It is not wrong to model it like in the following example:\r\n\r\n##### example\r\n\r\n```ts\r\ninterface ApiResponse\u003cT\u003e {\r\n   success: boolean\r\n   data: T | undefined\r\n   error: Error | undefined\r\n}\r\n```\r\n\r\nBut it doesn't work that well when you now want to access the `data` object because its type\r\ndefinition also contains `undefined`:\r\n\r\n```ts\r\nlet response: ApiResponse\u003cstring\u003e\r\n\r\nif (response.success) {\r\n   // `response.data` is of type `string | undefined`\r\n} else {\r\n   // `response.error` is of type `Error | undefined`\r\n}\r\n```\r\n\r\n##### improved example\r\n\r\nWe can improve the example by spitting our interface and then using an union type:\r\n\r\n```ts\r\n// will contain data but no Error\r\nexport interface SuccessResponse\u003cT\u003e {\r\n   success: true\r\n   data: T\r\n   error: undefined\r\n}\r\n\r\n// will contain an Error but no data\r\nexport interface ErrorResponse {\r\n   success: false\r\n   data: undefined\r\n   error: Error\r\n}\r\n\r\n// our union type\r\nexport type ApiResponse\u003cT\u003e = SuccessResponse\u003cT\u003e | ErrorResponse\r\n```\r\n\r\nIf we now access the `data` we will see that its type is no longer `undefined`:\r\n\r\n```ts\r\nlet response: ApiResponse\u003cstring\u003e\r\n\r\nif (response.success) {\r\n   // `response.data` is of type `string`\r\n} else {\r\n   // `response.error` is of type `Error`\r\n}\r\n```\r\n\r\nSo whenever you know that something is **either A or either B** you should also model it that way by\r\nsplitting the model into two different interfaces and then use an union type to.\r\n\r\n\u003e You can learn more about union types in the official\r\n\u003e [`TypeScript` documentation](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### #2 extend existing type definitions\r\n\r\nSometimes it is possible that a library contains missing or incomplete type definitions. You could\r\neither use `// @ts-ignore` comments and live with it or you can write the type declaration yourself.\r\n\r\nCreate a `*.d.ts` file somewhere in your `src` folder and use the following syntax:\r\n\r\n```ts\r\nimport 'package' // `'package'` is the library we want to extend\r\n\r\ndeclare module 'package' {\r\n   // we re-declare the module\r\n   // we add the missing function or override the existing one\r\n   export declare function someFunction(): boolean\r\n}\r\n```\r\n\r\nYou can now use it inside your code:\r\n\r\n```ts\r\nimport { someFunction } from 'package'\r\n\r\nconst result = someFunction()\r\n// result: boolean\r\n```\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### #3 wrap functions that have no strong typings\r\n\r\n\u003e [Example](https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/src/06-state-management/03-context-outsourced/slider-context.ts)\r\n\r\nWhenever you are using a function that has no or not so good `TypeScript` definitions or whenever\r\nyou need to cast something everytime you use a function, you should wrap it into a new function and\r\nadd type definitions there.\r\n\r\n##### example\r\n\r\n```ts\r\nimport { getContext } from 'svelte'\r\n\r\n// per default the return type is `unknown`\r\nconst c1 = getContext('my context')\r\n\r\n// we need to pass a generic to let `TypeScript` know what we expect\r\nconst c2 = getContext\u003cstring\u003e('my context')\r\n\r\n// oops typo!\r\nconst c3 = getContext\u003cstring\u003e('my comtext')\r\n```\r\n\r\nThe usage of `getContext` in the example above has three issues:\r\n\r\n1. you need to specify the return type whenever you call the function, so you would need to check\r\n   where the context gets set and copy the type definition from there\r\n2. when you refactor the context to hold different data, you need to update the type definition\r\n   everywhere\r\n3. you could easily introduce a typo because the parameter of the function is typed as a generic\r\n   string\r\n\r\n##### improved example\r\n\r\nWe can eliminate the issues mentioned above by wrapping `getContext` into a new function\r\n\r\n```ts\r\nimport { getContext } from 'svelte'\r\n\r\nconst getMyContext = () =\u003e getContext\u003cstring\u003e('my context')\r\n\r\nconst c = getMyContext() // typed as `string`\r\n```\r\n\r\nWe now have a single function that is responsible for the **type (1)** and the **context name (3)**.\r\nAnd we use that function when we want to access the data. When **refactoring (2)** we only need to\r\nchange it in a single place (and let `TypeScript` tell you if it's now getting used in a wrong way).\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### #4 use `opaque-types` for unique types\r\n\r\n\u003e [Example](https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/src/models/product.model.ts)\r\n\r\nSome types may look similar to another type but they are not actually related. If you are working\r\nwith databases the `ID` field would be such a case.\r\n\r\n##### example\r\n\r\nTyping the id of your DB model as a `string` is probably not wrong because from a technical\r\nperspective they are `strings`. But it is also a `string` for other models of your DB.\r\n\r\n```ts\r\ninterface Product {\r\n   id: string\r\n   author: string\r\n}\r\n\r\ninterface Category {\r\n   id: string\r\n   name: string\r\n}\r\n\r\nconst book: Product = { id: '1', /* ...rest */ }\r\nconst category: Category = { id: '1', /* ...rest */ }\r\n\r\nconst findById(productId): Product | undefined = { /* implementation */ }\r\n\r\nfindById(product.id) // valid\r\n\r\nfindById(category.id)  // we query the product DB with an id from the category DB\r\n// this will probably always return `undefined` if your DB uses random IDs\r\n```\r\n\r\nYou could introduce potential bugs if you are not careful. In the case above it probably is clear\r\nbecause we have named the data variables clearly, but what if you name the variable just `result`\r\nand then use the `findById` function. At first glance it looks good, but it is actually wrong.\r\n\r\n##### improved example\r\n\r\nWe can improve this by giving each model its unique ID type.\r\n\r\n```ts\r\n// in the next two lines we define an `opaque type` for our ProductId\r\ndeclare const _productId: unique symbol\r\nexport type ProductId = string \u0026 { readonly [_productId]: never }\r\n// we define our type as `string` but with additional meta-information\r\n// that tell TypeScript that this is a unique string\r\n\r\n// this type will behave like a `string`, so you can use it in functions that\r\n// expect a `string`, but it doesn't work the other way around. You can't pass\r\n// a normal `string` to a function that expects a certain `opaque type`\r\n\r\n// of course you can use `opaque types` also for `numbers` and other types\r\n\r\n\r\ninterface Product {\r\n   id: ProductId\r\n   author: string\r\n}\r\n\r\n// we also define a CategoryId as an `opaque type`\r\ndeclare const _categoryId: unique symbol\r\ntype CategoryId = string \u0026 { readonly [_categoryId]: never }\r\n\r\ninterface Category {\r\n   id: CategoryId\r\n   name: string\r\n}\r\n\r\n// in this case we need to cast it, because we are hardcoding the IDs\r\n// in a real world scenario the data gets loaded on runtime\r\n// from the DB and no casting is needed there\r\nconst book: Product = { id: '1' as ProductId, /* ...rest */ }\r\nconst category: Category = { id: '1' as CategoryId, /* ...rest */ }\r\n\r\nconst findById(id: ProductId): Product | undefined = { /* implementation */ }\r\n\r\nfindById(book.id) // valid\r\n\r\nfindById(category.id) // TypeScript shows an error:\r\n// Argument of type 'CategoryId' is not assignable to parameter of type 'ProductId'\r\n```\r\n\r\nNow `TypeScript` is able to tell you that something is wrong when you pass in the wrong ID.\r\n\r\nThis is not only useful for IDs but also for other `string` types that e.g. have a special meaning.\r\nSuch examples could be:\r\n\r\n-  `LocalizedString` to make sure a Button component only accepts internationalized strings\r\n-  `SanitizedHtmlString` so you know it contains no potential unsafe characters\r\n-  `ValidatedString` that tells you it's content was checked and marked as valid\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n#### #5 use `tagged template literals` to narrow down your string types\r\n\r\nLike you already have seen in the example from [TS-tipp #4](#4-use-opaque-types-for-unique-types),\r\nstrings are really generic and can hold any kind of data. Luckily `TypeScript` is flexible enough to\r\nlet us define which shape we expect the data.\r\n\r\n\u003e [Example](https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/src/models/tagged-template-literals.model.ts)\r\n\r\n```ts\r\n// a list of possible options (`enum`-like)\r\ntype Options = 'A' | 'B' | 'C'\r\n\r\n// `numbers` wrapped into a `string`\r\ntype NumberString = `${number}`\r\n\r\n// really simple check if the format looks like an email\r\ntype EmailString = `${string}@${string}.${string}`\r\n\r\n// simple check if it is an url\r\ntype LinkString = `http${'s' | ''}://${string}`\r\n\r\n// simple check if it could be in the format of an IPv4 address\r\n// note: we only can tell we expect any `number` but we cannot define value ranges yet\r\ntype IpStringSimple = `${number}.${number}.${number}.${number}`\r\n\r\n// useful if you are handling multiple date formats from different apis\r\ntype Api1DateTimeString = `${number}-${number}-${number} ${number}:${number}:${number}`\r\n// e.g. '2021-03-01 13:00:00'\r\n\r\ntype Api2Timestamp = `${number}` // e.g. '1635494400000'\r\n\r\n// ... and many more potential use-cases\r\n```\r\n\r\nIf we assign a value that doesn't match the definition of the shape `TypeScript` will throw an\r\nerror.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Conclusion\r\n\r\nYou should now have a feeling what is possible with `TypeScript` and how you can use it within your\r\n`Svelte` and `SvelteKit` applications. You probably have learned something new about `TypeScript` as\r\nwell. Just because the examples are listed here doesn't mean you **need** to type everything as\r\nstrict as possible. For some cases it makes sense to be stricter and sometimes beeing so strict will\r\nintroduce complexity you need to maintain over the lifetime of a project.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## JSDoc comments\r\n\r\nYou probably don't need `TypeScript` directly to profit from a strong type-checking experience\r\ninside your `Svelte` and `SvelteKit` applications. `VS Code` and the `Svelte` extension can also\r\noffer help if you annotate your components with [`JSDoc` comments](https://jsdoc.app/).\r\n\r\nHere is a simple example:\r\n\r\n-  JSDoc:\r\n\r\n   ```svelte\r\n   \u003cscript\u003e\r\n      /** @type {string} */\r\n      export let name\r\n   \u003c/script\u003e\r\n\r\n   Hello {name}!\r\n   ```\r\n\r\n-  TypeScript:\r\n\r\n   ```svelte\r\n   \u003cscript lang=\"ts\"\u003e\r\n      export let name: string\r\n   \u003c/script\u003e\r\n\r\n   Hello {name}!\r\n   ```\r\n\r\nIt is up to you which syntax you prefer. Some parts of the `SvelteKit` codebase are written in plain\r\n`JavaScript` files annotated with `JSDoc` comments.\r\n\r\nI would suggest directly using ´TypeScript´ because if you need more complex types, you will need to\r\nwrite\r\n[`declaration files`](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)\r\nin `TypeScript` syntax. It will be harder for you to write them if you are not used to the\r\n`TypeScript` syntax.\r\n\r\nAlso if you need to use generics inside your `JSDoc` comments, you may find the syntax a bit messy:\r\n\r\n-  JSDoc:\r\n\r\n```js\r\n/**\r\n * @typedef { import('./my-types').Type1 } Type1,\r\n * @typedef { import('./my-types').GenericType\u003cType1\u003e } Type1GenericType,\r\n */\r\n\r\n/**\r\n * @param {Type1GenericType} param\r\n * @returns {void}\r\n */\r\nexport const myFunction = (param) =\u003e {\r\n   // ... implementation\r\n}\r\n```\r\n\r\n-  TypeScript:\r\n\r\n```ts\r\nimport type { Type1, GenericType } from ('./my-types')\r\n\r\nexport const myFunction = (param: GenericType\u003cType1\u003e): void =\u003e {\r\n   // ... implementation\r\n}\r\n```\r\n\r\nSee the\r\n[official documentation](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) to\r\nlearn more about the `JSDoc`-syntax.\r\n\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\u003c!------------------------------------------------------------------------------------------------\u003e\r\n\r\n## Sponsors\r\n\r\n[Become a sponsor :heart:](https://github.com/sponsors/ivanhofer) if you want to support my open source contributions.\r\n\r\n\u003cp align=\"center\"\u003e\r\n   \u003ca href=\"https://cdn.jsdelivr.net/gh/ivanhofer/sponsors/sponsorkit/sponsors.svg\" title=\"ivanhofer's sponsors\"\u003e\r\n      \u003cimg src=\"https://cdn.jsdelivr.net/gh/ivanhofer/sponsors/sponsorkit/sponsors.svg\" alt=\"ivanhofer's sponsors\" /\u003e\r\n   \u003c/a\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n   Thanks for sponsoring my open source work!\r\n\u003c/p\u003e\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Fsveltekit-typescript-showcase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivanhofer%2Fsveltekit-typescript-showcase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Fsveltekit-typescript-showcase/lists"}