Skip to content

Type error when attaching an async function to an element #16138

Open
@theetrain

Description

@theetrain

Describe the bug

Problem

When providing an async function to @attach, svelte-check throws a type error:

Promise<void>; autoplay: true; playsinline: true; }' is not assignable to parameter of type 'HTMLProps<"video", HTMLAttributes<any>>'.
Type '{ [x: symbol]: (element: HTMLVideoElement) => Promise<void>; autoplay: true; playsinline: true; }' is not assignable to type 'Omit<HTMLVideoAttributes, never>'.
 'symbol' index signatures are incompatible.
   Type '(element: HTMLVideoElement) => Promise<void>' is not assignable to type 'false | Attachment<HTMLVideoElement> | null | undefined'.
     Type '(element: HTMLVideoElement) => Promise<void>' is not assignable to type 'Attachment<HTMLVideoElement>'.
       Type 'Promise<void>' is not assignable to type 'void | (() => void)'. (ts)

The async function is being consumed as:

<video {@attach init} autoplay={true} playsinline={true}></video>

This is due to @attach expecting functions that do not return a Promise. My understanding is @attach runs $effect, and $effect cannot track dependencies within async tasks ($effect: Understanding dependencies).

Workaround

A workaround I found is passing a wrapper function such as {@attach function(el) { init(el) }} since:

  • There are no longer type errors because we're passing a function definition that returns void and not a promise.
  • Reactive dependencies can be tracked, such as parameters passed to init.

This is a bug or 'working as intended' depending on interpretation.

Proposals

I suggest:

  • Add callout to @attach docs, mentioning async functions will not have their dependencies tracked in the @attach clause.
  • Update types to no longer throw during svelte-check.
  • Show suppressible warning whenever async functions are passed, reminding developers those methods won't track dependencies.

Alternative proposal:

  • Maintain current behaviour.
  • Provide clear alternatives alongside the type error, such as providing a wrapper method like I did in my workaround above.

Original thread on Discord: https://discord.com/channels/457912077277855764/1382427644448215080

Reproduction

Demo: https://stackblitz.com/edit/svelte-async-attach?file=src%2Flib%2FVideo.svelte

Video.svelte (the relevant parts)

<script lang="ts">
  let stream
  let error = $state('')

  async function init(element: HTMLVideoElement) {
    console.log('init', element)

    try {
      stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
      element.srcObject = stream;
    } catch (err) {
      if (err instanceof Error) {
        error = `Error accessing camera: ${err.name} - ${err.message}. Please ensure permissions are granted and you are on HTTPS or localhost.`;
      } else {
        error = 'Error accessing camera.';
      }
    }
  }
</script>

<!--
  svelte-ignore a11y_media_has_caption
-->
<video {@attach init} autoplay={true} playsinline={true}></video>

<p>{error}</p>
  1. Load demo
  2. Observe no runtime error
  3. Run npm run check
  4. Observe type error:
    Promise<void>; autoplay: true; playsinline: true; }' is not assignable to parameter of type 'HTMLProps<"video", HTMLAttributes<any>>'.
    Type '{ [x: symbol]: (element: HTMLVideoElement) => Promise<void>; autoplay: true; playsinline: true; }' is not assignable to type 'Omit<HTMLVideoAttributes, never>'.
     'symbol' index signatures are incompatible.
       Type '(element: HTMLVideoElement) => Promise<void>' is not assignable to type 'false | Attachment<HTMLVideoElement> | null | undefined'.
         Type '(element: HTMLVideoElement) => Promise<void>' is not assignable to type 'Attachment<HTMLVideoElement>'.
           Type 'Promise<void>' is not assignable to type 'void | (() => void)'. (ts)
    

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (4) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 20.19.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    svelte: ^5.25.0 => 5.33.19

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions