Skip to content

[Breaking] Remove flatten to simplify client types#108

Merged
just-be-dev merged 2 commits intomainfrom
no-flatten
Jan 29, 2025
Merged

[Breaking] Remove flatten to simplify client types#108
just-be-dev merged 2 commits intomainfrom
no-flatten

Conversation

@just-be-dev
Copy link
Copy Markdown
Owner

@just-be-dev just-be-dev commented Jan 29, 2025

As I've been working through the python client implementation WebViewOptions has been a pretty big complication due to the intersection of types with WebViewTarget. It's the kind of thing easy to express in typescript, but in python you've got to do weird inheritance shenanigans.

It's probably helpful if I provide more specifics. This is the typescript and python before this change

export type WebViewOptions =
  & {
	title: string;
    size?: WindowSize;
    devtools?: boolean;
	// ...others
  }
  & (
    | {
      headers?: Record<string, string>;
      url: string;
    }
    | {
      html: string;
      origin?: string;
    }
  );
class WebViewOptionsBase(msgspec.Struct, kw_only=True):
    title: str
    size: Optional[WindowSize] = None
    devtools: bool = False
    # ... others

class WebViewOptionsUrl(WebViewOptionsBase):
    url: str
    headers: Optional[dict[str, str]] = None

class WebViewOptionsHtml(WebViewOptionsBase):
    html: str
    origin: str = "init"

Because python doesn't have a good way to express type intersections like typescript does, the whole generation process is overly complicated. Even the typescript types are more complicated than I'd like though. It's a shame because it does make the API nice and simple.

This change removes the rust flatten that causes this weird intersection case and makes these options use a top level union property.

Now the generated typescript is just

export type WebViewOptions = {
	title: string;
    size?: WindowSize;
    devtools?: boolean;
    // the new field
    load?: WebViewLoadOptions;
	// ...others
  }

and the python will end up looking more like

class WebViewOptionsBase(msgspec.Struct, kw_only=True):
    title: str
    size: Optional[WindowSize] = None
    devtools: bool = False
    load: Optional[WebViewLoadOptions] = None
    # ... others

much more manageable. The tradeoff is, of course, the api surface area (specifically of deno).

Here's how the simple example gets updated

using webview = await createWebView({
  title: "Simple",
  devtools: true,
+  load: {
    html: "<h1>Hello, World!</h1>",
+  },
  initializationScript:
    "console.log('This is printed from initializationScript!')",
});

Gross, but reduces the overall complexity so very worth it. It's also worth noting that it removes the possibility of potential name collisions between the load options and the broader set of webview options.

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.

1 participant