fix: update onMount type definition to prevent async function return - #8136
Conversation
| * https://svelte.dev/docs#run-time-svelte-onmount | ||
| */ | ||
| export function onMount(fn: () => any) { | ||
| export function onMount(fn: (() => () => void) | (() => void | Promise<void>)) { |
There was a problem hiding this comment.
| export function onMount(fn: (() => () => void) | (() => void | Promise<void>)) { | |
| export function onMount(fn: (() => () => any) | (() => any | Promise<any>)) { |
In this case, this will be a type error, right?
But fundamentally, there is no problem.
// api.js
export const fetch = () => Promise.resolve({foo: 'bar'});
// Component.svelte
<script>
import { onMount } from 'svelte';
import { fetch } from './api';
onMount(() => fetch())
</script>So IMO, it's better to use any type instead of void.
There was a problem hiding this comment.
Thanks for the suggestion but I think that this won't work to prevent a function return from an async onMount.
I am attempting to cause a typescript failure on arguments of type () => Promise<() => any>, but the introduction of () => any means that this remains a valid argument.
Given I only actually want to disallow this single type, perhaps the following is more appropriate:
type AnyExcept<Type, Disallowed> = Type & (Type extends Disallowed ? never : Type);
export declare function onMount<T>(fn: () => AnyExcept<T, Promise<() => any>>) {Testing locally, this allows your example above and any other return from onMount except for the following:
onMount(async() => { // fails
return () => {
/// anything
}
})…ent-async-function-return
|
@baseballyama is attempting to deploy a commit to the Svelte Team on Vercel. A member of the Team first needs to authorize it. |
|
I updated the type. <script lang="ts">
import {onMount} from 'svelte';
// sync and no return (NO TYPE ERROR)
onMount(() => {
console.log("mounted");
});
// sync and return value (NO TYPE ERROR)
onMount(() => {
return 'done';
});
// sync and return sync (NO TYPE ERROR)
onMount(() => {
return () => {
return "done";
};
});
// sync and return async (NO TYPE ERROR)
onMount(() => {
return async () => {
const res = await fetch();
return res;
};
});
// async and no return (NO TYPE ERROR)
onMount(async () => {
await fetch();
});
// async and return value (NO TYPE ERROR)
onMount(async () => {
const res = await fetch();
return res;
});
// async and return sync (**TYPE ERROR**)
onMount(async () => {
return () => {
return "done";
};
});
// async and return async (**TYPE ERROR**)
onMount(async () => {
return async () => {
const res = await fetch();
return res;
};
});
</script> |
|
I added tests so we can check against regressions later. During that I noticed one case where there's now an error, which I'd like to discuss if this is a blocker from merging this, or if it's ok: If you return onMount(async () => {
const a: any = null as any;
return a;
});How likely is it that this happens? This probably only happens in the wild by accident through something like I'd be ok with this. Please vote thumbs up/down if you think this is ok/not ok. |
|
If this is just types and is not a runtime check for something that looks like a promise, I think this is fine. If someone really has a good/weird reason for going that, they can add a ts-ignore. |
…turn (#8136) --------- Co-authored-by: Yuichiro Yamashita <xydybaseball@gmail.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Breaking change / how to migrate
The
onMounttype definition was updated to make returning a function (orany, which could also be a function) asynchronously is a type error because in 90% of cases it hints at a mistake: You likely want the returned function to be called on destroy, but that does not happen for asynchronously returned functions.To migrate, adjust the code so you don't return a function asynchronously. Note that you also get the error if you return
anybecauseanyincludes function types.If this change revealed an actual bug in your code:
If this change results in a false positive, just make it not return a function:
PR description
From the docs for onMount (https://svelte.dev/docs#run-time-svelte-onmount) a cleanup function can be returned from onMount which will then be used on dismount for cleanup. These only work for synchronous functions, not async.
We just discovered a few mistakes in our codebase in which the following pattern was used:
I am proposing a types change to the onMount function to only accept the following function signatures:
() => void- async no cleanup() => Promise<void>- async no cleanup() => (() => void)- synchronous cleanupThis will catch mistakes where the cleanup would not be run.
Before submitting the PR, please make sure you do the following
[feat],[fix],[chore], or[docs].Tests
npm testand lint the project withnpm run lint