Repository: sveltejs/language-tools
Pull request #1050 (2021-06-11 09:54:58)

(feat) add dts output target to svelte2tsx
When setting the new mode option to 'dts', all tsx/jsx code and the template code will be thrown out, all shims will be inlined and the export is rewritten differently. Only the `code` property will be set on the returned element. Use this as an intermediate step to generate type definitions from a component. It is expected to pass the result to TypeScript which should handle emitting the d.ts files.


Patch:
diff --git a/packages/svelte2tsx/create-files.js b/packages/svelte2tsx/create-files.js
new file mode 100644
index 00000000..5c41bc22
--- /dev/null
+++ b/packages/svelte2tsx/create-files.js
@@ -0,0 +1,12 @@
+const fs = require('fs');
+
+let svelteShims = fs.readFileSync('./svelte-shims.d.ts', { encoding: 'utf-8' });
+svelteShims = svelteShims.substr(svelteShims.indexOf('declare class Sv')).replace(/`/g, '\\`');
+fs.writeFileSync(
+    './src/svelte2tsx/svelteShims.ts',
+    `/* eslint-disable */
+// Auto-generated, do not change
+// prettier-ignore
+export const svelteShims = \`${svelteShims}\`;
+`
+);
diff --git a/packages/svelte2tsx/index.d.ts b/packages/svelte2tsx/index.d.ts
index eb01dbe5..5c895dca 100644
--- a/packages/svelte2tsx/index.d.ts
+++ b/packages/svelte2tsx/index.d.ts
@@ -36,5 +36,13 @@ export default function svelte2tsx(
          * The namespace option from svelte config
          */
         namespace?: string;
+        /**
+         * When setting this to 'dts', all tsx/jsx code and the template code will be thrown out,
+         * all shims will be inlined and the component export is written differently.
+         * Only the `code` property will be set on the returned element.
+         * Use this as an intermediate step to generate type definitions from a component.
+         * It is expected to pass the result to TypeScript which should handle emitting the d.ts files.
+         */
+        mode?: 'tsx' | 'dts'
     }
 ): SvelteCompiledToTsx
diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json
index 7c98edb3..fac08011 100644
--- a/packages/svelte2tsx/package.json
+++ b/packages/svelte2tsx/package.json
@@ -44,10 +44,11 @@
         "typescript": "^4.1.2"
     },
     "scripts": {
-        "build": "rollup -c",
+        "build": "npm run create-files && rollup -c",
         "prepublishOnly": "npm run build",
-        "dev": "rollup -c -w",
-        "test": "mocha test/test.ts"
+        "dev": "npm run create-files && rollup -c -w",
+        "test": "npm run create-files && mocha test/test.ts",
+        "create-files": "node ./create-files.js"
     },
     "files": [
         "index.mjs",
diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts
index a25ff3a4..2430e54b 100644
--- a/packages/svelte2tsx/src/svelte2tsx/index.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/index.ts
@@ -25,6 +25,7 @@ import {
 } from './processInstanceScriptContent';
 import { processModuleScriptTag } from './processModuleScriptTag';
 import { ScopeStack } from './utils/Scope';
+import { svelteShims } from './svelteShims';
 
 interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
     str: MagicString;
@@ -49,6 +50,7 @@ interface AddComponentExportPara {
     exportedNames: ExportedNames;
     fileName?: string;
     componentDocumentation: ComponentDocumentation;
+    mode: 'dts' | 'tsx';
 }
 
 type TemplateProcessResult = {
@@ -320,7 +322,8 @@ function addComponentExport({
     usesAccessors,
     exportedNames,
     fileName,
-    componentDocumentation
+    componentDocumentation,
+    mode
 }: AddComponentExportPara) {
     const eventsDef = strictEvents ? 'render()' : '__sveltets_with_any_event(render())';
     let propDef = '';
@@ -336,15 +339,30 @@ function addComponentExport({
     }
 
     const doc = componentDocumentation.getFormatted();
-    const className = fileName && classNameFromFilename(fileName);
-
-    const statement =
-        `\n\n${doc}export default class${
-            className ? ` ${className}` : ''
-        } extends createSvelte2TsxComponent(${propDef}) {` +
-        createClassGetters(getters) +
-        (usesAccessors ? createClassAccessors(getters, exportedNames) : '') +
-        '\n}';
+    const className = fileName && classNameFromFilename(fileName, mode !== 'dts');
+
+    let statement: string;
+    if (mode === 'dts') {
+        statement =
+            `\nconst __propDef = ${propDef};\n` +
+            `export type ${className}Props = typeof __propDef.props;\n` +
+            `export type ${className}Events = typeof __propDef.events;\n` +
+            `export type ${className}Slots = typeof __propDef.slots;\n` +
+            `\n${doc}export default class${
+                className ? ` ${className}` : ''
+            } extends SvelteComponentTyped<${className}Props, ${className}Events, ${className}Slots> {` + // eslint-disable-line max-len
+            createClassGetters(getters) +
+            (usesAccessors ? createClassAccessors(getters, exportedNames) : '') +
+            '\n}';
+    } else {
+        statement =
+            `\n\n${doc}export default class${
+                className ? ` ${className}` : ''
+            } extends createSvelte2TsxComponent(${propDef}) {` +
+            createClassGetters(getters) +
+            (usesAccessors ? createClassAccessors(getters, exportedNames) : '') +
+            '\n}';
+    }
 
     str.append(statement);
 }
@@ -355,7 +373,7 @@ function addComponentExport({
  *
  * https://svelte.dev/docs#Tags
  */
-function classNameFromFilename(filename: string): string | undefined {
+function classNameFromFilename(filename: string, appendSuffix: boolean): string | undefined {
     try {
         const withoutExtensions = path.parse(filename).name?.split('.')[0];
         const withoutInvalidCharacters = withoutExtensions
@@ -371,7 +389,7 @@ function classNameFromFilename(filename: string): string | undefined {
         const withoutLeadingInvalidCharacters = withoutInvalidCharacters.substr(firstValidCharIdx);
         const inPascalCase = pascalCase(withoutLeadingInvalidCharacters);
         const finalName = firstValidCharIdx === -1 ? `A${inPascalCase}` : inPascalCase;
-        return `${finalName}${COMPONENT_SUFFIX}`;
+        return `${finalName}${appendSuffix ? COMPONENT_SUFFIX : ''}`;
     } catch (error) {
         console.warn(`Failed to create a name for the component class from filename ${filename}`);
         return undefined;
@@ -453,12 +471,13 @@ function createRenderFunction({
 
 export function svelte2tsx(
     svelte: string,
-    options?: {
+    options: {
         filename?: string;
         isTsFile?: boolean;
         emitOnTemplateError?: boolean;
         namespace?: string;
-    }
+        mode?: 'tsx' | 'dts';
+    } = {}
 ) {
     const str = new MagicString(svelte);
     // process the htmlx as a svelte template
@@ -545,15 +564,32 @@ export function svelte2tsx(
         exportedNames,
         usesAccessors,
         fileName: options?.filename,
-        componentDocumentation
+        componentDocumentation,
+        mode: options.mode
     });
 
-    str.prepend('///<reference types="svelte" />\n');
-
-    return {
-        code: str.toString(),
-        map: str.generateMap({ hires: true, source: options?.filename }),
-        exportedNames,
-        events: events.createAPI()
-    };
+    if (options.mode === 'dts') {
+        // Prepend the import and all shims so the file is self-contained.
+        // TypeScript's dts generation will remove the unused parts later.
+        str.prepend('import { SvelteComponentTyped } from "svelte"\n' + svelteShims + '\n');
+        let code = str.toString();
+        // Remove all tsx occurences and the template part from the output
+        code =
+            code
+                .substr(0, code.indexOf('\n() => (<>'))
+                // prepended before each script block
+                .replace('<></>;', '')
+                .replace('<></>;', '') + code.substr(code.lastIndexOf('</>);') + '</>);'.length);
+        return {
+            code
+        };
+    } else {
+        str.prepend('///<reference types="svelte" />\n');
+        return {
+            code: str.toString(),
+            map: str.generateMap({ hires: true, source: options?.filename }),
+            exportedNames,
+            events: events.createAPI()
+        };
+    }
 }
diff --git a/packages/svelte2tsx/src/svelte2tsx/svelteShims.ts b/packages/svelte2tsx/src/svelte2tsx/svelteShims.ts
new file mode 100644
index 00000000..9c67eec3
--- /dev/null
+++ b/packages/svelte2tsx/src/svelte2tsx/svelteShims.ts
@@ -0,0 +1,204 @@
+/* eslint-disable */
+// Auto-generated, do not change
+// prettier-ignore
+export const svelteShims = `declare class Svelte2TsxComponent<
+    Props extends {} = {},
+    Events extends {} = {},
+    Slots extends {} = {}
+> {
+    // svelte2tsx-specific
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$prop_def: Props;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$events_def: Events;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$slot_def: Slots;
+    // https://svelte.dev/docs#Client-side_component_API
+    constructor(options: Svelte2TsxComponentConstructorParameters<Props>);
+    /**
+     * Causes the callback function to be called whenever the component dispatches an event.
+     * A function is returned that will remove the event listener when called.
+     */
+    $on<K extends keyof Events & string>(event: K, handler: (e: Events[K]) => any): () => void;
+    /**
+     * Removes a component from the DOM and triggers any \`onDestroy\` handlers.
+     */
+    $destroy(): void;
+    /**
+     * Programmatically sets props on an instance.
+     * \`component.$set({ x: 1 })\` is equivalent to \`x = 1\` inside the component's \`<script>\` block.
+     * Calling this method schedules an update for the next microtask — the DOM is __not__ updated synchronously.
+     */
+    $set(props?: Partial<Props>): void;
+    // From SvelteComponent(Dev) definition
+    $$: any;
+    $capture_state(): void;
+    $inject_state(): void;
+}
+
+interface Svelte2TsxComponentConstructorParameters<Props extends {}> {
+    /**
+     * An HTMLElement to render to. This option is required.
+     */
+    target: Element;
+    /**
+     * A child of \`target\` to render the component immediately before.
+     */
+    anchor?: Element;
+    /**
+     * An object of properties to supply to the component.
+     */
+    props?: Props;
+    hydrate?: boolean;
+    intro?: boolean;
+    $$inline?: boolean;
+}
+
+type AConstructorTypeOf<T, U extends any[] = any[]> = new (...args: U) => T;
+type SvelteComponentConstructor<T, U extends Svelte2TsxComponentConstructorParameters<any>> = new (options: U) => T;
+
+type SvelteActionReturnType = {
+	update?: (args: any) => void,
+	destroy?: () => void
+} | void
+
+type SvelteTransitionConfig = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteTransitionReturnType = SvelteTransitionConfig | (() => SvelteTransitionConfig)
+
+type SvelteAnimationReturnType = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteWithOptionalProps<Props, Keys extends keyof Props> = Omit<Props, Keys> & Partial<Pick<Props, Keys>>;
+type SvelteAllProps = { [index: string]: any }
+type SveltePropsAnyFallback<Props> = {[K in keyof Props]: Props[K] extends undefined ? any : Props[K]}
+type SvelteRestProps = { [index: string]: any }
+type SvelteSlots = { [index: string]: any }
+type SvelteStore<T> = { subscribe: (run: (value: T) => any, invalidate?: any) => any }
+
+// Forces TypeScript to look into the type which results in a better representation of it
+// which helps for error messages
+type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
+
+declare var process: NodeJS.Process & { browser: boolean }
+declare var __sveltets_AnimationMove: { from: DOMRect, to: DOMRect }
+
+declare function __sveltets_ensureAnimation(animationCall: SvelteAnimationReturnType): {};
+declare function __sveltets_ensureAction(actionCall: SvelteActionReturnType): {};
+declare function __sveltets_ensureTransition(transitionCall: SvelteTransitionReturnType): {};
+declare function __sveltets_ensureFunction(expression: (e: Event & { detail?: any }) => unknown ): {};
+declare function __sveltets_ensureType<T>(type: AConstructorTypeOf<T>, el: T): {};
+declare function __sveltets_cssProp(prop: Record<string, any>): {};
+declare function __sveltets_ctorOf<T>(type: T): AConstructorTypeOf<T>;
+declare function __sveltets_instanceOf<T = any>(type: AConstructorTypeOf<T>): T;
+declare function __sveltets_allPropsType(): SvelteAllProps
+declare function __sveltets_restPropsType(): SvelteRestProps
+declare function __sveltets_slotsType<Slots, Key extends keyof Slots>(slots: Slots): Record<Key, boolean>;
+
+// Overload of the following two functions is necessary.
+// An empty array of optionalProps makes OptionalProps type any, which means we lose the prop typing.
+// optionalProps need to be first or its type cannot be infered correctly.
+
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props>, events?: Events, slots?: Slots }
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>>, events?: Events, slots?: Slots }
+
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props> & SvelteAllProps, events?: Events, slots?: Slots }
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>> & SvelteAllProps, events?: Events, slots?: Slots }
+
+
+declare function __sveltets_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props & SvelteAllProps, events?: Events, slots?: Slots }
+
+declare function __sveltets_with_any_event<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props, events?: Events & {[evt: string]: CustomEvent<any>;}, slots?: Slots }
+
+declare function __sveltets_store_get<T = any>(store: SvelteStore<T>): T
+declare function __sveltets_any(dummy: any): any;
+declare function __sveltets_empty(dummy: any): {};
+declare function __sveltets_componentType(): AConstructorTypeOf<Svelte2TsxComponent<any, any, any>>
+declare function __sveltets_invalidate<T>(getValue: () => T): T
+
+declare function __sveltets_mapWindowEvent<K extends keyof HTMLBodyElementEventMap>(
+    event: K
+): HTMLBodyElementEventMap[K];
+declare function __sveltets_mapBodyEvent<K extends keyof WindowEventMap>(
+    event: K
+): WindowEventMap[K];
+declare function __sveltets_mapElementEvent<K extends keyof HTMLElementEventMap>(
+    event: K
+): HTMLElementEventMap[K];
+declare function __sveltets_mapElementTag<K extends keyof ElementTagNameMap>(
+    tag: K
+): ElementTagNameMap[K];
+declare function __sveltets_mapElementTag<K extends keyof SVGElementTagNameMap>(
+    tag: K
+): SVGElementTagNameMap[K];
+declare function __sveltets_mapElementTag(
+    tag: any
+): HTMLElement;
+
+declare function __sveltets_bubbleEventDef<Events, K extends keyof Events>(
+    events: Events, eventKey: K
+): Events[K];
+declare function __sveltets_bubbleEventDef(
+    events: any, eventKey: string
+): any;
+
+declare const __sveltets_customEvent: CustomEvent<any>;
+declare function __sveltets_toEventTypings<Typings>(): {[Key in keyof Typings]: CustomEvent<Typings[Key]>};
+
+declare function __sveltets_unionType<T1, T2>(t1: T1, t2: T2): T1 | T2;
+declare function __sveltets_unionType<T1, T2, T3>(t1: T1, t2: T2, t3: T3): T1 | T2 | T3;
+declare function __sveltets_unionType<T1, T2, T3, T4>(t1: T1, t2: T2, t3: T3, t4: T4): T1 | T2 | T3 | T4;
+declare function __sveltets_unionType(...types: any[]): any;
+
+declare function __sveltets_awaitThen<T>(
+    promise: T,
+    onfulfilled: (value: T extends PromiseLike<infer U> ? U : T) => any,
+    onrejected?: (value: T extends PromiseLike<any> ? any : never) => any
+): any;
+
+declare function __sveltets_each<T>(
+    array: ArrayLike<T>,
+    callbackfn: (value: T, index: number) => any
+): any;
+
+declare function createSvelte2TsxComponent<Props, Events, Slots>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): SvelteComponentConstructor<Svelte2TsxComponent<Props, Events, Slots>,Svelte2TsxComponentConstructorParameters<Props>>;
+
+declare function __sveltets_unwrapArr<T>(arr: ArrayLike<T>): T
+declare function __sveltets_unwrapPromiseLike<T>(promise: PromiseLike<T> | T): T
+`;


Test patch:
diff --git a/packages/svelte2tsx/test/helpers.ts b/packages/svelte2tsx/test/helpers.ts
index 7f7b083c..d80cbaba 100644
--- a/packages/svelte2tsx/test/helpers.ts
+++ b/packages/svelte2tsx/test/helpers.ts
@@ -226,7 +226,8 @@ export function test_samples(dir: string, transform: TransformSampleFn, jsx: 'js
             filename: svelteFile,
             sampleName: sample.name,
             emitOnTemplateError: false,
-            preserveAttributeCase: sample.name.endsWith('-foreign-ns')
+            preserveAttributeCase: sample.name.endsWith('-foreign-ns'),
+            mode: sample.name.endsWith('-dts') ? 'dts' : undefined
         };
 
         if (process.env.CI) {
@@ -307,7 +308,8 @@ export function get_svelte2tsx_config(base: BaseConfig, sampleName: string): Sve
         filename: base.filename,
         emitOnTemplateError: base.emitOnTemplateError,
         isTsFile: sampleName.startsWith('ts-'),
-        namespace: sampleName.endsWith('-foreign-ns') ? 'foreign' : null
+        namespace: sampleName.endsWith('-foreign-ns') ? 'foreign' : null,
+        mode: sampleName.endsWith('-dts') ? 'dts' : undefined
     };
 }
 
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/expected.tsx
new file mode 100644
index 00000000..2816db6d
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/expected.tsx
@@ -0,0 +1,226 @@
+import { SvelteComponentTyped } from "svelte"
+declare class Svelte2TsxComponent<
+    Props extends {} = {},
+    Events extends {} = {},
+    Slots extends {} = {}
+> {
+    // svelte2tsx-specific
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$prop_def: Props;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$events_def: Events;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$slot_def: Slots;
+    // https://svelte.dev/docs#Client-side_component_API
+    constructor(options: Svelte2TsxComponentConstructorParameters<Props>);
+    /**
+     * Causes the callback function to be called whenever the component dispatches an event.
+     * A function is returned that will remove the event listener when called.
+     */
+    $on<K extends keyof Events & string>(event: K, handler: (e: Events[K]) => any): () => void;
+    /**
+     * Removes a component from the DOM and triggers any `onDestroy` handlers.
+     */
+    $destroy(): void;
+    /**
+     * Programmatically sets props on an instance.
+     * `component.$set({ x: 1 })` is equivalent to `x = 1` inside the component's `<script>` block.
+     * Calling this method schedules an update for the next microtask — the DOM is __not__ updated synchronously.
+     */
+    $set(props?: Partial<Props>): void;
+    // From SvelteComponent(Dev) definition
+    $$: any;
+    $capture_state(): void;
+    $inject_state(): void;
+}
+
+interface Svelte2TsxComponentConstructorParameters<Props extends {}> {
+    /**
+     * An HTMLElement to render to. This option is required.
+     */
+    target: Element;
+    /**
+     * A child of `target` to render the component immediately before.
+     */
+    anchor?: Element;
+    /**
+     * An object of properties to supply to the component.
+     */
+    props?: Props;
+    hydrate?: boolean;
+    intro?: boolean;
+    $$inline?: boolean;
+}
+
+type AConstructorTypeOf<T, U extends any[] = any[]> = new (...args: U) => T;
+type SvelteComponentConstructor<T, U extends Svelte2TsxComponentConstructorParameters<any>> = new (options: U) => T;
+
+type SvelteActionReturnType = {
+	update?: (args: any) => void,
+	destroy?: () => void
+} | void
+
+type SvelteTransitionConfig = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteTransitionReturnType = SvelteTransitionConfig | (() => SvelteTransitionConfig)
+
+type SvelteAnimationReturnType = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteWithOptionalProps<Props, Keys extends keyof Props> = Omit<Props, Keys> & Partial<Pick<Props, Keys>>;
+type SvelteAllProps = { [index: string]: any }
+type SveltePropsAnyFallback<Props> = {[K in keyof Props]: Props[K] extends undefined ? any : Props[K]}
+type SvelteRestProps = { [index: string]: any }
+type SvelteSlots = { [index: string]: any }
+type SvelteStore<T> = { subscribe: (run: (value: T) => any, invalidate?: any) => any }
+
+// Forces TypeScript to look into the type which results in a better representation of it
+// which helps for error messages
+type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
+
+declare var process: NodeJS.Process & { browser: boolean }
+declare var __sveltets_AnimationMove: { from: DOMRect, to: DOMRect }
+
+declare function __sveltets_ensureAnimation(animationCall: SvelteAnimationReturnType): {};
+declare function __sveltets_ensureAction(actionCall: SvelteActionReturnType): {};
+declare function __sveltets_ensureTransition(transitionCall: SvelteTransitionReturnType): {};
+declare function __sveltets_ensureFunction(expression: (e: Event & { detail?: any }) => unknown ): {};
+declare function __sveltets_ensureType<T>(type: AConstructorTypeOf<T>, el: T): {};
+declare function __sveltets_cssProp(prop: Record<string, any>): {};
+declare function __sveltets_ctorOf<T>(type: T): AConstructorTypeOf<T>;
+declare function __sveltets_instanceOf<T = any>(type: AConstructorTypeOf<T>): T;
+declare function __sveltets_allPropsType(): SvelteAllProps
+declare function __sveltets_restPropsType(): SvelteRestProps
+declare function __sveltets_slotsType<Slots, Key extends keyof Slots>(slots: Slots): Record<Key, boolean>;
+
+// Overload of the following two functions is necessary.
+// An empty array of optionalProps makes OptionalProps type any, which means we lose the prop typing.
+// optionalProps need to be first or its type cannot be infered correctly.
+
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props>, events?: Events, slots?: Slots }
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>>, events?: Events, slots?: Slots }
+
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props> & SvelteAllProps, events?: Events, slots?: Slots }
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>> & SvelteAllProps, events?: Events, slots?: Slots }
+
+
+declare function __sveltets_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props & SvelteAllProps, events?: Events, slots?: Slots }
+
+declare function __sveltets_with_any_event<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props, events?: Events & {[evt: string]: CustomEvent<any>;}, slots?: Slots }
+
+declare function __sveltets_store_get<T = any>(store: SvelteStore<T>): T
+declare function __sveltets_any(dummy: any): any;
+declare function __sveltets_empty(dummy: any): {};
+declare function __sveltets_componentType(): AConstructorTypeOf<Svelte2TsxComponent<any, any, any>>
+declare function __sveltets_invalidate<T>(getValue: () => T): T
+
+declare function __sveltets_mapWindowEvent<K extends keyof HTMLBodyElementEventMap>(
+    event: K
+): HTMLBodyElementEventMap[K];
+declare function __sveltets_mapBodyEvent<K extends keyof WindowEventMap>(
+    event: K
+): WindowEventMap[K];
+declare function __sveltets_mapElementEvent<K extends keyof HTMLElementEventMap>(
+    event: K
+): HTMLElementEventMap[K];
+declare function __sveltets_mapElementTag<K extends keyof ElementTagNameMap>(
+    tag: K
+): ElementTagNameMap[K];
+declare function __sveltets_mapElementTag<K extends keyof SVGElementTagNameMap>(
+    tag: K
+): SVGElementTagNameMap[K];
+declare function __sveltets_mapElementTag(
+    tag: any
+): HTMLElement;
+
+declare function __sveltets_bubbleEventDef<Events, K extends keyof Events>(
+    events: Events, eventKey: K
+): Events[K];
+declare function __sveltets_bubbleEventDef(
+    events: any, eventKey: string
+): any;
+
+declare const __sveltets_customEvent: CustomEvent<any>;
+declare function __sveltets_toEventTypings<Typings>(): {[Key in keyof Typings]: CustomEvent<Typings[Key]>};
+
+declare function __sveltets_unionType<T1, T2>(t1: T1, t2: T2): T1 | T2;
+declare function __sveltets_unionType<T1, T2, T3>(t1: T1, t2: T2, t3: T3): T1 | T2 | T3;
+declare function __sveltets_unionType<T1, T2, T3, T4>(t1: T1, t2: T2, t3: T3, t4: T4): T1 | T2 | T3 | T4;
+declare function __sveltets_unionType(...types: any[]): any;
+
+declare function __sveltets_awaitThen<T>(
+    promise: T,
+    onfulfilled: (value: T extends PromiseLike<infer U> ? U : T) => any,
+    onrejected?: (value: T extends PromiseLike<any> ? any : never) => any
+): any;
+
+declare function __sveltets_each<T>(
+    array: ArrayLike<T>,
+    callbackfn: (value: T, index: number) => any
+): any;
+
+declare function createSvelte2TsxComponent<Props, Events, Slots>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): SvelteComponentConstructor<Svelte2TsxComponent<Props, Events, Slots>,Svelte2TsxComponentConstructorParameters<Props>>;
+
+declare function __sveltets_unwrapArr<T>(arr: ArrayLike<T>): T
+declare function __sveltets_unwrapPromiseLike<T>(promise: PromiseLike<T> | T): T
+
+
+    export const foo = 'foo';
+;
+import { createEventDispatcher } from 'svelte';
+function render() {
+
+  
+
+  /** @type {boolean} */
+   let bar;
+   let foobar = '';
+
+  const dispatch = createEventDispatcher();
+  dispatch('hi');
+;
+return { props: {
+/** @type {boolean} */bar: bar , foobar: foobar}, slots: {'default': {bar:bar}}, getters: {}, events: {'click':__sveltets_mapElementEvent('click'), 'hi': __sveltets_customEvent} }}
+const __propDef = __sveltets_partial(['foobar'], __sveltets_with_any_event(render()));
+export type InputProps = typeof __propDef.props;
+export type InputEvents = typeof __propDef.events;
+export type InputSlots = typeof __propDef.slots;
+
+export default class Input extends SvelteComponentTyped<InputProps, InputEvents, InputSlots> {
+}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/input.svelte
new file mode 100644
index 00000000..bcaed4c6
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/creates-dts/input.svelte
@@ -0,0 +1,17 @@
+<script context="module">
+    export const foo = 'foo';
+</script>
+
+<script>
+  import { createEventDispatcher } from 'svelte';
+
+  /** @type {boolean} */
+  export let bar;
+  export let foobar = '';
+
+  const dispatch = createEventDispatcher();
+  dispatch('hi');
+</script>
+
+<button on:click>hi</button>
+<slot {bar}></slot>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/expected.tsx
new file mode 100644
index 00000000..169fa215
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/expected.tsx
@@ -0,0 +1,225 @@
+import { SvelteComponentTyped } from "svelte"
+declare class Svelte2TsxComponent<
+    Props extends {} = {},
+    Events extends {} = {},
+    Slots extends {} = {}
+> {
+    // svelte2tsx-specific
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$prop_def: Props;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$events_def: Events;
+    /**
+     * @internal This is for type checking capabilities only
+     * and does not exist at runtime. Don't use this property.
+     */
+    $$slot_def: Slots;
+    // https://svelte.dev/docs#Client-side_component_API
+    constructor(options: Svelte2TsxComponentConstructorParameters<Props>);
+    /**
+     * Causes the callback function to be called whenever the component dispatches an event.
+     * A function is returned that will remove the event listener when called.
+     */
+    $on<K extends keyof Events & string>(event: K, handler: (e: Events[K]) => any): () => void;
+    /**
+     * Removes a component from the DOM and triggers any `onDestroy` handlers.
+     */
+    $destroy(): void;
+    /**
+     * Programmatically sets props on an instance.
+     * `component.$set({ x: 1 })` is equivalent to `x = 1` inside the component's `<script>` block.
+     * Calling this method schedules an update for the next microtask — the DOM is __not__ updated synchronously.
+     */
+    $set(props?: Partial<Props>): void;
+    // From SvelteComponent(Dev) definition
+    $$: any;
+    $capture_state(): void;
+    $inject_state(): void;
+}
+
+interface Svelte2TsxComponentConstructorParameters<Props extends {}> {
+    /**
+     * An HTMLElement to render to. This option is required.
+     */
+    target: Element;
+    /**
+     * A child of `target` to render the component immediately before.
+     */
+    anchor?: Element;
+    /**
+     * An object of properties to supply to the component.
+     */
+    props?: Props;
+    hydrate?: boolean;
+    intro?: boolean;
+    $$inline?: boolean;
+}
+
+type AConstructorTypeOf<T, U extends any[] = any[]> = new (...args: U) => T;
+type SvelteComponentConstructor<T, U extends Svelte2TsxComponentConstructorParameters<any>> = new (options: U) => T;
+
+type SvelteActionReturnType = {
+	update?: (args: any) => void,
+	destroy?: () => void
+} | void
+
+type SvelteTransitionConfig = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteTransitionReturnType = SvelteTransitionConfig | (() => SvelteTransitionConfig)
+
+type SvelteAnimationReturnType = {
+    delay?: number,
+    duration?: number,
+    easing?: (t: number) => number,
+    css?: (t: number, u: number) => string,
+    tick?: (t: number, u: number) => void
+}
+
+type SvelteWithOptionalProps<Props, Keys extends keyof Props> = Omit<Props, Keys> & Partial<Pick<Props, Keys>>;
+type SvelteAllProps = { [index: string]: any }
+type SveltePropsAnyFallback<Props> = {[K in keyof Props]: Props[K] extends undefined ? any : Props[K]}
+type SvelteRestProps = { [index: string]: any }
+type SvelteSlots = { [index: string]: any }
+type SvelteStore<T> = { subscribe: (run: (value: T) => any, invalidate?: any) => any }
+
+// Forces TypeScript to look into the type which results in a better representation of it
+// which helps for error messages
+type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
+
+declare var process: NodeJS.Process & { browser: boolean }
+declare var __sveltets_AnimationMove: { from: DOMRect, to: DOMRect }
+
+declare function __sveltets_ensureAnimation(animationCall: SvelteAnimationReturnType): {};
+declare function __sveltets_ensureAction(actionCall: SvelteActionReturnType): {};
+declare function __sveltets_ensureTransition(transitionCall: SvelteTransitionReturnType): {};
+declare function __sveltets_ensureFunction(expression: (e: Event & { detail?: any }) => unknown ): {};
+declare function __sveltets_ensureType<T>(type: AConstructorTypeOf<T>, el: T): {};
+declare function __sveltets_cssProp(prop: Record<string, any>): {};
+declare function __sveltets_ctorOf<T>(type: T): AConstructorTypeOf<T>;
+declare function __sveltets_instanceOf<T = any>(type: AConstructorTypeOf<T>): T;
+declare function __sveltets_allPropsType(): SvelteAllProps
+declare function __sveltets_restPropsType(): SvelteRestProps
+declare function __sveltets_slotsType<Slots, Key extends keyof Slots>(slots: Slots): Record<Key, boolean>;
+
+// Overload of the following two functions is necessary.
+// An empty array of optionalProps makes OptionalProps type any, which means we lose the prop typing.
+// optionalProps need to be first or its type cannot be infered correctly.
+
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props>, events?: Events, slots?: Slots }
+declare function __sveltets_partial<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>>, events?: Events, slots?: Slots }
+
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: SveltePropsAnyFallback<Props> & SvelteAllProps, events?: Events, slots?: Slots }
+declare function __sveltets_partial_with_any<Props = {}, Events = {}, Slots = {}, OptionalProps extends keyof Props = any>(
+    optionalProps: OptionalProps[],
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Expand<SvelteWithOptionalProps<SveltePropsAnyFallback<Props>, OptionalProps>> & SvelteAllProps, events?: Events, slots?: Slots }
+
+
+declare function __sveltets_with_any<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props & SvelteAllProps, events?: Events, slots?: Slots }
+
+declare function __sveltets_with_any_event<Props = {}, Events = {}, Slots = {}>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): {props?: Props, events?: Events & {[evt: string]: CustomEvent<any>;}, slots?: Slots }
+
+declare function __sveltets_store_get<T = any>(store: SvelteStore<T>): T
+declare function __sveltets_any(dummy: any): any;
+declare function __sveltets_empty(dummy: any): {};
+declare function __sveltets_componentType(): AConstructorTypeOf<Svelte2TsxComponent<any, any, any>>
+declare function __sveltets_invalidate<T>(getValue: () => T): T
+
+declare function __sveltets_mapWindowEvent<K extends keyof HTMLBodyElementEventMap>(
+    event: K
+): HTMLBodyElementEventMap[K];
+declare function __sveltets_mapBodyEvent<K extends keyof WindowEventMap>(
+    event: K
+): WindowEventMap[K];
+declare function __sveltets_mapElementEvent<K extends keyof HTMLElementEventMap>(
+    event: K
+): HTMLElementEventMap[K];
+declare function __sveltets_mapElementTag<K extends keyof ElementTagNameMap>(
+    tag: K
+): ElementTagNameMap[K];
+declare function __sveltets_mapElementTag<K extends keyof SVGElementTagNameMap>(
+    tag: K
+): SVGElementTagNameMap[K];
+declare function __sveltets_mapElementTag(
+    tag: any
+): HTMLElement;
+
+declare function __sveltets_bubbleEventDef<Events, K extends keyof Events>(
+    events: Events, eventKey: K
+): Events[K];
+declare function __sveltets_bubbleEventDef(
+    events: any, eventKey: string
+): any;
+
+declare const __sveltets_customEvent: CustomEvent<any>;
+declare function __sveltets_toEventTypings<Typings>(): {[Key in keyof Typings]: CustomEvent<Typings[Key]>};
+
+declare function __sveltets_unionType<T1, T2>(t1: T1, t2: T2): T1 | T2;
+declare function __sveltets_unionType<T1, T2, T3>(t1: T1, t2: T2, t3: T3): T1 | T2 | T3;
+declare function __sveltets_unionType<T1, T2, T3, T4>(t1: T1, t2: T2, t3: T3, t4: T4): T1 | T2 | T3 | T4;
+declare function __sveltets_unionType(...types: any[]): any;
+
+declare function __sveltets_awaitThen<T>(
+    promise: T,
+    onfulfilled: (value: T extends PromiseLike<infer U> ? U : T) => any,
+    onrejected?: (value: T extends PromiseLike<any> ? any : never) => any
+): any;
+
+declare function __sveltets_each<T>(
+    array: ArrayLike<T>,
+    callbackfn: (value: T, index: number) => any
+): any;
+
+declare function createSvelte2TsxComponent<Props, Events, Slots>(
+    render: {props?: Props, events?: Events, slots?: Slots }
+): SvelteComponentConstructor<Svelte2TsxComponent<Props, Events, Slots>,Svelte2TsxComponentConstructorParameters<Props>>;
+
+declare function __sveltets_unwrapArr<T>(arr: ArrayLike<T>): T
+declare function __sveltets_unwrapPromiseLike<T>(promise: PromiseLike<T> | T): T
+
+
+    export const foo = 'foo';
+;
+import Bar from './bar';
+import { createEventDispatcher } from 'svelte';
+function render() {
+
+  
+  
+
+   let bar: Bar;
+   let foobar = '';
+
+  const dispatch = createEventDispatcher<{swipe: string}>();
+;
+return { props: {bar: bar , foobar: foobar} as {bar: Bar, foobar?: typeof foobar}, slots: {'default': {bar:bar}}, getters: {}, events: {...__sveltets_toEventTypings<{swipe: string}>(), 'click':__sveltets_mapElementEvent('click')} }}
+const __propDef = __sveltets_with_any_event(render());
+export type InputProps = typeof __propDef.props;
+export type InputEvents = typeof __propDef.events;
+export type InputSlots = typeof __propDef.slots;
+
+export default class Input extends SvelteComponentTyped<InputProps, InputEvents, InputSlots> {
+}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/input.svelte
new file mode 100644
index 00000000..ab7d0c09
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-creates-dts/input.svelte
@@ -0,0 +1,16 @@
+<script lang="ts" context="module">
+    export const foo = 'foo';
+</script>
+
+<script lang="ts">
+  import Bar from './bar';
+  import { createEventDispatcher } from 'svelte';
+
+  export let bar: Bar;
+  export let foobar = '';
+
+  const dispatch = createEventDispatcher<{swipe: string}>();
+</script>
+
+<button on:click>hi</button>
+<slot {bar}></slot>
\ No newline at end of file