sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
80.24k stars 4.27k forks source link

Svelte 5: $state.snapshot() messes up the type of state with promises inside #13837

Open xl0 opened 1 month ago

xl0 commented 1 month ago

Describe the bug

https://svelte.dev/playground/hello-world?version=5.0.5#H4sIAAAAAAAAA22QQWvDMAyF_4rm9eCU0HQ7pklgsMOOgw12mHdwMyU1deQQqetGyH8fSRYorAeD_en56Um9qpxHVul7r-SnRZVOQMWKbDO-Htp2w1_oZWR7y3iNl4EESVilKuOyc62At1TnRgkbVRgyZMSRYFfZEuFFrCD0IzTSpvDchcYxZiydo3qUyzAVyYhHgf8SyIHwvFCtwzGCvABGeXUNhpNoPYFw1Ea9hc5_3hgVxXC_3W6jaLcYc_qXJYcVjxfdt8NUJiMrrCosF6feSBmIg8eND7We5Rsm2_IhiOYo2sEQLamrE5XiAoHYI_LUQy_NomXyZA0xrJOLcbNk3t68McoOd9MyntD7AP2tPVsn0IIckKAZoB9PMtFh_DzLKdufRAJBoNK78pj38wQXUa6kH4rHABwalIOjOktmj8KQipXgt6hUuhMOH8MvMdbmKDICAAA=

Related to #13798

Code

<script lang="ts">

    interface State {
        p: Promise<string>
    }

    let p: Promise<string> = new Promise((ok) => setTimeout(() => ok("World!"), 2000));
    let s: State = $state({p});

    $effect(() => { console.log($state.snapshot(s)); })

    function takesState(s: State) {
        /* , */
    }

</script>

<h1>
    Hello {#await p then m} {m} {/await}
</h1>

<button onclick={() => takesState($state.snapshot(s))}>Do something</button>

Typescript warning:

Argument of type '{ p: { then: {}; catch: {}; finally: {}; readonly [Symbol.toStringTag]: string; }; }' is not assignable to parameter of type 'State'.
  The types of 'p.then' are incompatible between these types.
    Type '{}' is not assignable to type '<TResult1 = string, TResult2 = never>(onfulfilled?: ((value: string) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => Promise<...>'.
      Type '{}' provides no match for the signature '<TResult1 = string, TResult2 = never>(onfulfilled?: ((value: string) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined): Promise<...>'.ts(2345)

Processed with Pretty TypeScript Errors: https://ts-error-translator.vercel.app/?error=IIJw5grgtgpgdgFwAQHsBmSEE8AOMkDkA3kjgFxIkIAW8FRAvgNxIDGAhgq9fc0mgEs47ADYisvFiBjsAJijjikAbQDKWKACMUIgHQIUqhCCFgAKuzABdCgGdjplnwYEkA20jgpk7W7YFgwpoi+Aak7CDssAgwIKgY2HiERpwwBLoAUEhIZrSYuDAe6IQ4+rRwrhH4QqwoUDicAsH4mjAIAO4w8Ji0tqEFtpnZ2WYFhIyu7p7eSL7+gezNmCj5SQQAPGYASoUQIggAjEgAvEj2JnBgADQ5O7Z7CABMJ54wAG6xAHwAFApoe4IxDBZAB+Cjfb5vUQQGB2ByXACUJ0+t12+yOAB8kAAFEB1dwwAAyAgA1jBNncHgdPkisXA9iIkFiIHBZDBBHBgTcFNIAFYwVgxUHg77SXwKCjsOBYJHHFHbNFPJk4vFQAnEsnrXTamnK+liZUstkc4GylG4-F9LU69JZYYjMbEFykPFvARsjxeJBQTjcfgoOI0fDzYQICDSQgUxVHU7nUw3BX3fbPU6cj4gH5-AECIHCpAQqEiGFwi5gM2opOHZUWtV9DXkxNU3V0hmG1nsoRc1BwPkCoVg-OimS2CWzaXlxvJ6uq9Wk8na3TNzyt5ntk2yBEUGsE62L9JAA

Reproduction

.

Logs

No response

System Info

5.0.5

Severity

Big annoyance

xl0 commented 1 month ago

Temporary solution app.d.ts:

declare global {
    namespace $state {
        type MyPrimitive = string | number | boolean | null | undefined | Promise;

        type MySnapshot<T> = T extends MyPrimitive
            ? T
            : T extends Cloneable
                ? NonReactive<T>
                : T extends { toJSON(): infer R }
                    ? R
                    : T extends Array<infer U>
                        ? Array<Snapshot<U>>
                        : T extends object
                            ? T extends { [key: string]: unknown }
                                ? { [K in keyof T]: Snapshot<T[K]> }
                                : never
                            : never;

        export function snapshot<T>(state: T): MySnapshot<T>;
    }