Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Looking for API ideas: Typed closure handle for BridgeJS #541

Copy link
Copy link
@kateinoigakukun

Description

@kateinoigakukun
Issue body actions

Summary

BridgeJS needs a typed closure handle type that preserves object identity across the Swift/JS bridge. We're looking for community input on naming.

Background

JavaScriptKit already has JSClosure for untyped closures:

let closure = JSClosure { args -> JSValue in
    // ...
}

For BridgeJS, we need a typed variant with a generic parameter for the function signature. This enables type-safe declarations like:

@JSFunction func addEventListener(_ type: String, _ listener: (MessageEvent) -> Void)

The typed closure handle is essential for APIs like addEventListener/removeEventListener that require stable object identity, passing the same closure reference to both calls.

Related issues

Constraints

  1. Must coexist with JSClosure: we can't simply make JSClosure<T> generic (breaking change)
  2. Explicit conversion required: Swift doesn't support implicit conversion from closure literals, so users will write the type name frequently

Candidates

1. Nested type: JSClosure.Typed<T>

@JSClass struct JSHTMLElement {
    @JSFunction func addEventListener(_ type: String, _ listener: JSClosure.Typed<(MessageEvent) -> Void>)
    @JSFunction func removeEventListener(_ type: String, _ listener: JSClosure.Typed<(MessageEvent) -> Void>)
}

let handler = JSClosure.Typed<(MessageEvent) -> Void> { event in
    print(event.data)
}
document.addEventListener("load", handler)
document.removeEventListener("load", handler)

@JSFunction func requestAnimationFrame(_ callback: JSOneshotClosure.Typed<(Double) -> Void>)

requestAnimationFrame(JSOneshotClosure.Typed { timestamp in
  print(timestamp)
})
  • Clear relationship to JSClosure
  • Discoverable via autocomplete
  • Verbose (15 characters)

2. JSTypedClosure<T>

@JSClass struct JSHTMLElement {
    @JSFunction func addEventListener(_ type: String, _ listener: JSTypedClosure<(MessageEvent) -> Void>)
    @JSFunction func removeEventListener(_ type: String, _ listener: JSTypedClosure<(MessageEvent) -> Void>)
}

let handler = JSTypedClosure<(MessageEvent) -> Void> { event in
    print(event.data)
}
document.addEventListener("load", handler)
document.removeEventListener("load", handler)

@JSFunction func requestAnimationFrame(_ callback: JSTypedClosure<(Double) -> Void>)

requestAnimationFrame(JSTypedClosure.oneshot { timestamp in
  print(timestamp)
})

3. JSCallback<T>

@JSClass struct JSHTMLElement {
    @JSFunction func addEventListener(_ type: String, _ listener: JSCallback<(MessageEvent) -> Void>)
    @JSFunction func removeEventListener(_ type: String, _ listener: JSCallback<(MessageEvent) -> Void>)
}

let handler = JSCallback<(MessageEvent) -> Void> { event in
    print(event.data)
}
document.addEventListener("load", handler)
document.removeEventListener("load", handler)

@JSFunction func requestAnimationFrame(_ callback: JSCallback.Oneshot<(Double) -> Void>)

requestAnimationFrame(JSCallback.Oneshot { timestamp in
  print(timestamp)
})

3. [Need your input]

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.