breaking: update onMount type definition to prevent async function return (#8136)

---------

Co-authored-by: Yuichiro Yamashita <xydybaseball@gmail.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/8515/head
Chris Kerr 1 year ago committed by Simon Holthausen
parent 2f423475f7
commit 236ffa833d

@ -6,6 +6,7 @@
* **breaking** Minimum supported TypeScript version is now 5 (it will likely work with lower versions, but we make no guarantess about that) * **breaking** Minimum supported TypeScript version is now 5 (it will likely work with lower versions, but we make no guarantess about that)
* **breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224)) * **breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224)) * **breaking** Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions (see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136))
* Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391)) * Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391))
* Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251)) * Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251))
* Bind `null` option and input values consistently ([#8312](https://github.com/sveltejs/svelte/issues/8312)) * Bind `null` option and input values consistently ([#8312](https://github.com/sveltejs/svelte/issues/8312))

@ -27,11 +27,13 @@ export function beforeUpdate(fn: () => any) {
* It must be called during the component's initialisation (but doesn't need to live *inside* the component; * It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module). * it can be called from an external module).
* *
* If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted.
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). * `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
* *
* https://svelte.dev/docs#run-time-svelte-onmount * https://svelte.dev/docs#run-time-svelte-onmount
*/ */
export function onMount(fn: () => any) { export function onMount<T>(fn: () => T extends Promise<() => any> ? "Returning a function asynchronously from onMount won't call that function on destroy" : T): void {
get_current_component().$$.on_mount.push(fn); get_current_component().$$.on_mount.push(fn);
} }

@ -3,15 +3,15 @@ import type { Action, ActionReturn } from '$runtime/action';
// ---------------- Action // ---------------- Action
const href: Action<HTMLAnchorElement> = (node) => { const href: Action<HTMLAnchorElement> = (node) => {
node.href = ''; node.href = '';
// @ts-expect-error // @ts-expect-error
node.href = 1; node.href = 1;
}; };
href; href;
const required: Action<HTMLElement, boolean> = (node, param) => { const required: Action<HTMLElement, boolean> = (node, param) => {
node; node;
param; param;
}; };
required(null as any, true); required(null as any, true);
// @ts-expect-error (only in strict mode) boolean missing // @ts-expect-error (only in strict mode) boolean missing
@ -20,34 +20,34 @@ required(null as any);
required(null as any, 'string'); required(null as any, 'string');
const required1: Action<HTMLElement, boolean> = (node, param) => { const required1: Action<HTMLElement, boolean> = (node, param) => {
node; node;
param; param;
return { return {
update: (p) => p === true, update: (p) => p === true,
destroy: () => {} destroy: () => {}
}; };
}; };
required1; required1;
const required2: Action<HTMLElement, boolean> = (node) => { const required2: Action<HTMLElement, boolean> = (node) => {
node; node;
}; };
required2; required2;
const required3: Action<HTMLElement, boolean> = (node, param) => { const required3: Action<HTMLElement, boolean> = (node, param) => {
node; node;
param; param;
return { return {
// @ts-expect-error comparison always resolves to false // @ts-expect-error comparison always resolves to false
update: (p) => p === 'd', update: (p) => p === 'd',
destroy: () => {} destroy: () => {}
}; };
}; };
required3; required3;
const optional: Action<HTMLElement, boolean | undefined> = (node, param?) => { const optional: Action<HTMLElement, boolean | undefined> = (node, param?) => {
node; node;
param; param;
}; };
optional(null as any, true); optional(null as any, true);
optional(null as any); optional(null as any);
@ -55,39 +55,39 @@ optional(null as any);
optional(null as any, 'string'); optional(null as any, 'string');
const optional1: Action<HTMLElement, boolean | undefined> = (node, param?) => { const optional1: Action<HTMLElement, boolean | undefined> = (node, param?) => {
node; node;
param; param;
return { return {
update: (p) => p === true, update: (p) => p === true,
destroy: () => {} destroy: () => {}
}; };
}; };
optional1; optional1;
const optional2: Action<HTMLElement, boolean | undefined> = (node) => { const optional2: Action<HTMLElement, boolean | undefined> = (node) => {
node; node;
}; };
optional2; optional2;
const optional3: Action<HTMLElement, boolean | undefined> = (node, param) => { const optional3: Action<HTMLElement, boolean | undefined> = (node, param) => {
node; node;
param; param;
}; };
optional3; optional3;
const optional4: Action<HTMLElement, boolean | undefined> = (node, param?) => { const optional4: Action<HTMLElement, boolean | undefined> = (node, param?) => {
node; node;
param; param;
return { return {
// @ts-expect-error comparison always resolves to false // @ts-expect-error comparison always resolves to false
update: (p) => p === 'd', update: (p) => p === 'd',
destroy: () => {} destroy: () => {}
}; };
}; };
optional4; optional4;
const no: Action<HTMLElement, never> = (node) => { const no: Action<HTMLElement, never> = (node) => {
node; node;
}; };
// @ts-expect-error second param // @ts-expect-error second param
no(null as any, true); no(null as any, true);
@ -96,10 +96,10 @@ no(null as any);
no(null as any, 'string'); no(null as any, 'string');
const no1: Action<HTMLElement, never> = (node) => { const no1: Action<HTMLElement, never> = (node) => {
node; node;
return { return {
destroy: () => {} destroy: () => {}
}; };
}; };
no1; no1;
@ -113,32 +113,32 @@ no3;
// @ts-expect-error update method given // @ts-expect-error update method given
const no4: Action<HTMLElement, never> = (node) => { const no4: Action<HTMLElement, never> = (node) => {
return { return {
update: () => {}, update: () => {},
destroy: () => {} destroy: () => {}
}; };
}; };
no4; no4;
// ---------------- ActionReturn // ---------------- ActionReturn
const requiredReturn: ActionReturn<string> = { const requiredReturn: ActionReturn<string> = {
update: (p) => p.toString() update: (p) => p.toString()
}; };
requiredReturn; requiredReturn;
const optionalReturn: ActionReturn<boolean | undefined> = { const optionalReturn: ActionReturn<boolean | undefined> = {
update: (p) => { update: (p) => {
p === true; p === true;
// @ts-expect-error could be undefined // @ts-expect-error could be undefined
p.toString(); p.toString();
} }
}; };
optionalReturn; optionalReturn;
const invalidProperty: ActionReturn = { const invalidProperty: ActionReturn = {
// @ts-expect-error invalid property // @ts-expect-error invalid property
invalid: () => {} invalid: () => {}
}; };
invalidProperty; invalidProperty;

@ -1,10 +1,10 @@
import { createEventDispatcher } from '$runtime/internal/lifecycle'; import { createEventDispatcher } from '$runtime/internal/lifecycle';
const dispatch = createEventDispatcher<{ const dispatch = createEventDispatcher<{
loaded: never loaded: never
change: string change: string
valid: boolean valid: boolean
optional: number | null optional: number | null
}>(); }>();
// @ts-expect-error: dispatch invalid event // @ts-expect-error: dispatch invalid event

@ -0,0 +1,58 @@
import { onMount } from '$runtime/index';
// sync and no return
onMount(() => {
console.log('mounted');
});
// sync and return value
onMount(() => {
return 'done';
});
// sync and return sync
onMount(() => {
return () => {
return 'done';
};
});
// sync and return async
onMount(() => {
return async () => {
const res = await fetch('');
return res;
};
});
// async and no return
onMount(async () => {
await fetch('');
});
// async and return value
onMount(async () => {
const res = await fetch('');
return res;
});
// @ts-expect-error async and return sync
onMount(async () => {
return () => {
return 'done';
};
});
// @ts-expect-error async and return async
onMount(async () => {
return async () => {
const res = await fetch('');
return res;
};
});
// @ts-expect-error async and return any
onMount(async () => {
const a: any = null as any;
return a;
});
Loading…
Cancel
Save