Skip to content

Conversation

krodak
Copy link
Contributor

@krodak krodak commented Sep 10, 2025

Introduction

This PR adds support for Swift Optionals when exporting Swift funcionality to JS / TS using BridgeJS plugin.

Overview

  • Swift Optionals are represented by T | null union, as null explicitly represents intentional absent of value, which aligns with Swift's nil.
  • for each parameter additional "isSome" flag is used to represent optionality.
  • new shared extension Optionals were added to BridgeJSIntrinsics to keep generated Swift glue code sparse and reuse existing patterns. This required new protocols for case and associated enum value types, which
  • for support of return values, new bjs module methods were added for JS lowering _swift_js_return_optional_<type>
  • support for different Optional syntax was added (including typealiasing), so all of the below should work identically:
@JS func test(name: String?) -> String?
@JS func test(name: Optional<String>) -> Optional<String>
@JS func test(name: Swift.Optional<String>) -> Swift.Optional<String>
@JS func test(name: Swift.Optional<  String >) -> Swift.Optional< String. >
typealias OptionalName = String?
@JS func test(name: OptionalName) -> OptionalName

Examples

@JS class UserService {
    var userId: Int? 
    func findUser(id: Int) -> User?
    func process(data: String?) -> String
}

Generated TypeScript:

export interface UserService extends SwiftHeapObject {
    userId: number | null;
    findUser(id: number | null): User | null;
    process(data: string | null): string;
}

Testing

Added tests for different scenarios for all supported optional data types, including parameters, properties and return value usage.

Documentation

Extended current documentation with new Exporting-Swift-Optional.md

BridgeJS: Optional case enum, optional raw enum support
BridgeJS: Simplify JS glue code as much as possible + basic docs
@krodak krodak self-assigned this Sep 10, 2025
@krodak krodak force-pushed the feat/optionals-support branch 2 times, most recently from 3f55206 to 8ecbbd4 Compare September 10, 2025 12:56
@krodak krodak force-pushed the feat/optionals-support branch from 8ecbbd4 to 6091dfc Compare September 10, 2025 12:59
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive support for Swift Optional types when exporting Swift functionality to JavaScript/TypeScript using the BridgeJS plugin. Swift optionals are mapped to T | null union types in TypeScript, using null to represent the absence of a value which aligns with Swift's nil semantics.

Key changes include:

  • Support for all optional syntax forms: T?, Optional<T>, Swift.Optional<T>, and type aliases
  • Bidirectional optional handling for parameters, return values, and class properties
  • New intrinsic functions for optional type bridging and lowering operations

Reviewed Changes

Copilot reviewed 57 out of 65 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Sources/JavaScriptKit/BridgeJSInstrincics.swift Adds extensive optional bridging support with new protocols and extension methods
Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Optional.md Comprehensive documentation for optional types usage
Tests/BridgeJSRuntimeTests/ExportAPITests.swift Test functions for all optional type scenarios
Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift Updates type system to include optional case
Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift Core optional type resolution and code generation logic
Comments suppressed due to low confidence (1)

Sources/JavaScriptKit/BridgeJSInstrincics.swift:1

  • The function should be marked as consuming like other similar functions in this file to match the pattern used elsewhere for bridge lowering functions.
/// BridgeJS Intrinsics

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@JS func test(name: String?) -> String? { return name }
@JS func test(name: Optional<String>) -> Optional<String> { return name }
@JS func test(name: Swift.Optional<String>) -> Swift.Optional<String> { return name }
@JS func test(name: Swift.Optional< String >) -> Swift.Optional< String. > { return name }
Copy link
Preview

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo with an extra period before the closing angle bracket: String. > should be String >.

Suggested change
@JS func test(name: Swift.Optional< String >) -> Swift.Optional< String. > { return name }
@JS func test(name: Swift.Optional< String >) -> Swift.Optional< String > { return name }

Copilot uses AI. Check for mistakes.

Comment on lines +129 to +131
tmpRetString = null;
} else {
tmpRetString = swift.memory.getObject(objectId);
Copy link
Preview

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong variable assignment: should assign to tmpRetString for object return storage, but this should likely be a different variable for optional object returns.

Copilot uses AI. Check for mistakes.

Copy link
Member

@kateinoigakukun kateinoigakukun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job 👏

@kateinoigakukun kateinoigakukun merged commit 79a0a4e into swiftwasm:main Sep 10, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants