Skip to content

Commit

Permalink
provide more specific interface, type guards from client package
Browse files Browse the repository at this point in the history
  • Loading branch information
turbocrime committed Jul 12, 2024
1 parent f111f8d commit b0666ea
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-spiders-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/client': minor
---

provide type guards for event, and better types for event interface
42 changes: 40 additions & 2 deletions packages/client/src/event.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { PenumbraInjectionState, PenumbraSymbol } from './index.js';

export class PenumbraInjectionStateEvent extends CustomEvent<{
export interface PenumbraInjectionStateEventDetail {
origin: string;
state?: PenumbraInjectionState;
}> {
}

export class PenumbraInjectionStateEvent extends CustomEvent<PenumbraInjectionStateEventDetail> {
constructor(injectionProviderOrigin: string, injectionState?: PenumbraInjectionState) {
super('penumbrastate', {
detail: {
Expand All @@ -13,3 +15,39 @@ export class PenumbraInjectionStateEvent extends CustomEvent<{
});
}
}

export const isPenumbraInjectionStateEvent = (evt: Event): evt is PenumbraInjectionStateEvent =>
evt instanceof PenumbraInjectionStateEvent ||
('detail' in evt && isPenumbraInjectionStateEventDetail(evt.detail));

export const isPenumbraInjectionStateEventDetail = (
detail: unknown,
): detail is PenumbraInjectionStateEventDetail =>
typeof detail === 'object' &&
detail !== null &&
'origin' in detail &&
typeof detail.origin === 'string';

// utility type for SpecificEventTarget. any and unused are required for type inference
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ParametersTail<T extends (...args: any[]) => any> =
Parameters<T> extends [unknown, ...infer TailParams] ? TailParams : never;

// like EventTarget, but restricts possible event types
interface SpecificEventTarget<SpecificTypeName extends string, SpecificEvent extends Event = Event>
extends EventTarget {
addEventListener: (
type: SpecificTypeName,
...rest: ParametersTail<EventTarget['addEventListener']>
) => void;
removeEventListener: (
type: SpecificTypeName,
...rest: ParametersTail<EventTarget['removeEventListener']>
) => void;
dispatchEvent: (event: SpecificEvent) => boolean;
}

export type PenumbraInjectionStateEventTarget = Omit<
SpecificEventTarget<'penumbrastate', never>,
'dispatchEvent'
>;
15 changes: 10 additions & 5 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PenumbraInjectionStateEventTarget } from './event.js';

export * from './error.js';
export * from './event.js';

Expand Down Expand Up @@ -38,7 +40,7 @@ export const PenumbraSymbol = Symbol.for('penumbra');
* use the helpers available in `@penumbra-zone/client/create`.
*
*/
export interface PenumbraInjection {
export interface PenumbraInjection extends Readonly<PenumbraInjectionStateEventTarget> {
/** Should contain a URI at the provider's origin, serving a manifest
* describing this provider. */
readonly manifest: string;
Expand All @@ -63,11 +65,14 @@ export interface PenumbraInjection {
/** Synchronously return present injection state. */
readonly state: () => PenumbraInjectionState;

/** Emits `PenubraInjectionStateEvent` when state changes. Listen for
/** Like a normal EventTarget.addEventListener, but should only emit
* `PenubraInjectionStateEvent` when state changes. Listen for
* `'penumbrastate'` events, and check the `detail` field for a
* `PenumbraInjectionState` value. */
readonly addEventListener: EventTarget['addEventListener'];
readonly removeEventListener: EventTarget['removeEventListener'];
* `PenumbraInjectionState` value.
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
*/
readonly addEventListener: PenumbraInjectionStateEventTarget['addEventListener'];
readonly removeEventListener: PenumbraInjectionStateEventTarget['addEventListener'];
}

export enum PenumbraInjectionState {
Expand Down

0 comments on commit b0666ea

Please sign in to comment.