specta-rs / tauri-specta

Completely typesafe Tauri commands
MIT License
368 stars 40 forks source link

Fix Event.emit type error for enums #128

Closed bgschiller closed 1 month ago

bgschiller commented 1 month ago

Rust enums are represented in TS as a type union. For some reason, T extends null collapses the type into an intersection, which makes it a type error to call SomeEvent.emit(x) for any value x. That may be a TypeScript error—I'm still investigating. In the meantime, this patch avoids the issue.

Here's a minimal example to try in the typescript playground. The definition of __EventObj__ is as it is before my patch.

// These definitions come from @tauri-apps/api/event
interface Event<T> {
    /** Event name */
    event: EventName;
    /** Event identifier used to unlisten */
    id: number;
    /** Event payload */
    payload: T;
}
declare function emit(event: string, payload?: unknown): Promise<void>;
type EventCallback<T> = (event: Event<T>) => void;
type EventName = string & Record<never, never>

// This is generated by tauri-specta based on my rust types
export type SocketAction =
  | { MessageForClient: { conn_id: string; message: string } }
  | { CloseSocket: { conn_id: string } }

// This is also from tauri-specta, but doesn't depend on my types
type __EventObj__<T> = {
  emit: T extends null
    ? (payload?: T) => ReturnType<typeof emit>
    : (payload: T) => ReturnType<typeof emit>
}

// pretend we have one of these
declare const eo: __EventObj__<SocketAction>;

const x = { MessageForClient: { conn_id: 'string', message: 'str' }} satisfies SocketAction

eo.emit(x) // 💥 Type error
oscartbeaumont commented 1 month ago

For anyone coming across this I think this comment is a solid explanation for why this change makes sense.

Personally was not aware of it until now.