From 1003acccc4a7446dc84d287038fe9c8d0bc12f40 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:52:54 +0100 Subject: [PATCH] feat: add Snippet type (#9584) * feat: add Snippet type related to #9447 * one more test --- .changeset/shiny-baboons-play.md | 5 +++ .../src/compiler/phases/1-parse/acorn.js | 1 - .../src/compiler/phases/1-parse/ambient.d.ts | 3 ++ packages/svelte/src/main/public.d.ts | 13 ++++++ packages/svelte/tests/types/snippet.ts | 40 +++++++++++++++++++ packages/svelte/tsconfig.json | 2 +- 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 .changeset/shiny-baboons-play.md create mode 100644 packages/svelte/src/compiler/phases/1-parse/ambient.d.ts create mode 100644 packages/svelte/tests/types/snippet.ts diff --git a/.changeset/shiny-baboons-play.md b/.changeset/shiny-baboons-play.md new file mode 100644 index 0000000000..dad945f3db --- /dev/null +++ b/.changeset/shiny-baboons-play.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: add Snippet type diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 0db95bdee0..dfc87d90af 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -2,7 +2,6 @@ import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; import { tsPlugin } from 'acorn-typescript'; -// @ts-expect-error const ParserWithTS = acorn.Parser.extend(tsPlugin()); /** diff --git a/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts b/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts new file mode 100644 index 0000000000..8243dd0a42 --- /dev/null +++ b/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts @@ -0,0 +1,3 @@ +// Silence the acorn typescript errors through this ambient type definition + tsconfig.json path alias +// That way we can omit `"skipLibCheck": true` and catch other errors in our d.ts files +declare module 'acorn-typescript'; diff --git a/packages/svelte/src/main/public.d.ts b/packages/svelte/src/main/public.d.ts index f6d1ab7571..e98a627342 100644 --- a/packages/svelte/src/main/public.d.ts +++ b/packages/svelte/src/main/public.d.ts @@ -185,6 +185,19 @@ export type ComponentType = (new ( element?: typeof HTMLElement; }; +declare const SnippetReturn: unique symbol; + +/** + * 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. + */ +export interface Snippet { + (arg: T): typeof SnippetReturn; +} + interface DispatchOptions { cancelable?: boolean; } diff --git a/packages/svelte/tests/types/snippet.ts b/packages/svelte/tests/types/snippet.ts new file mode 100644 index 0000000000..5a1e46c241 --- /dev/null +++ b/packages/svelte/tests/types/snippet.ts @@ -0,0 +1,40 @@ +import type { Snippet } from 'svelte'; + +const return_type: ReturnType = null as any; + +// @ts-expect-error +const a: Snippet<{ text: string }> = () => {}; +// @ts-expect-error +const b: Snippet = (a, b) => { + return return_type; +}; +// @ts-expect-error +const c: Snippet = (a: string) => { + return return_type; +}; +// @ts-expect-error +const d: Snippet = (a: string, b: number) => { + return return_type; +}; +// @ts-expect-error +const e: Snippet = (a: string) => { + return return_type; +}; +const f: Snippet = (a) => { + // @ts-expect-error + a?.x; + return return_type; +}; +const g: Snippet = (a) => { + // @ts-expect-error + a === ''; + a === true; + return return_type; +}; +const h: Snippet<{ a: true }> = (a) => { + a.a === true; + return return_type; +}; +const i: Snippet = () => { + return return_type; +}; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index dc8c2d134b..97f8819b28 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -10,12 +10,12 @@ "noErrorTruncation": true, "allowSyntheticDefaultImports": true, "verbatimModuleSyntax": true, - "skipLibCheck": true, "types": ["node"], "strict": true, "allowJs": true, "checkJs": true, "paths": { + "acorn-typescript": ["./src/compiler/phases/1-parse/ambient.d.ts"], "svelte": ["./src/main/public.d.ts"], "svelte/action": ["./src/action/public.d.ts"], "svelte/compiler": ["./src/compiler/public.d.ts"],