TypeScript
Where the types come from, which ones exist, and the two that need narrowing.
@wajub/js ships its own definitions. There is no @types/ package to install and nothing to
configure. The framework packages export the same types, so installing one of them is enough:
import type { PaymentSuccess } from '@wajub/react' works.
npm install @wajub/jsEach entry point is typed.
| Import | Types | Side effect |
|---|---|---|
@wajub/js | Included | Loads Wajub.js on import |
@wajub/js/pure | Included | None until you call loadWajub() |
https://js.wajub.com | Globals only, see below | Loads Wajub.js |
Everything worth importing
import type {
EmbeddedConfig,
PopupConfig,
CheckoutInstance,
PopupInstance,
ComponentConfig,
ComponentFieldsConfig,
ComponentInstance,
ComponentsFactory,
ComponentType,
ConfirmPaymentOptions,
} from '@wajub/js';import type {
AppearanceConfig,
AppearanceVariables,
AppearanceRules,
AppearanceThemePreset,
AppearanceColorScheme,
AppearanceLabels,
CheckoutLayout,
CheckoutState,
EmbedBreakdown,
PaymentSuccess,
SessionPreview,
ClientSignals,
ComponentChangeEvent,
ComponentRedirectEvent,
ComponentStateChangeEvent,
ComponentEventMap,
InheritSource,
ComponentAddressValue,
AddressValue,
AddressMode,
CollectAddressMode,
PhoneFieldMode,
WajubError,
WajubErrorType,
} from '@wajub/js';import type {
WajubRuntime,
WajubSDK,
WajubClient,
WajubFactory,
WajubInitOptions,
CreatePaymentParams,
CreatePaymentResult,
InitCheckoutOptions,
InitCheckoutResult,
} from '@wajub/js';Two places where narrowing matters
The helpers can return null. Everything imported from @wajub/js resolves to null when
there is no window, which is what makes a server render safe. TypeScript will make you handle it.
Two exceptions, componentEstimatedHeight() and componentReserveHeight(), are synchronous and
return a number on the server too.
import { loadWajub } from '@wajub/js/pure';
const runtime = await loadWajub();
if (!runtime) return;
const checkout = runtime.wajub.mount('#checkout', { sessionId });Once you hold the runtime, its methods are synchronous and never return null, so this is the
only check you need.
The error callbacks are not typed as WajubError. They receive
WajubError | Record<string, unknown>. Narrow before reading code.
import { loadWajub, mount } from '@wajub/js';
const runtime = await loadWajub();
await mount('#checkout', {
sessionId,
onError: (error) => {
if (runtime && error instanceof runtime.WajubError) {
report(error.code, error.retryable);
return;
}
report('unknown_error', false);
},
});Take the WajubError class from what loadWajub() resolves to, as above, not from an import of
@wajub/js. Everything else on this page is a type only import.
Event payloads
on(event, fn) and off(event, fn) type the payload of every event a component emits, through
ComponentEventMap: statechange hands a ComponentStateChangeEvent, redirect a
ComponentRedirectEvent, resize a { height }. The matching ComponentConfig callbacks,
onStateChange, onResize and onRedirect, are typed the same way.
component.on('redirect', ({ url, method }) => {
// The page leaves right after: a beacon survives it, a fetch may not.
navigator.sendBeacon('/analytics/left-for-provider', JSON.stringify({ host: new URL(url).host, method }));
});Server rendering
With a framework package, there is nothing to do. The code below is for wiring @wajub/js
yourself.
'use client';
import { useEffect, useRef } from 'react';
import { loadWajub } from '@wajub/js/pure';
import type { CheckoutInstance } from '@wajub/js';
export function Checkout({ sessionId }: { sessionId: string }) {
const host = useRef<HTMLDivElement>(null);
const instance = useRef<CheckoutInstance | null>(null);
useEffect(() => {
loadWajub().then((runtime) => {
if (!runtime || !host.current) return;
instance.current = runtime.wajub.mount(host.current, { sessionId });
});
return () => instance.current?.destroy();
}, [sessionId]);
return <div ref={host} />;
}Import from /pure, so that importing the module does nothing on the server.
To keep a payment field's room in that server render, give its container
componentEstimatedHeight(type, variant, fields) as its min-height, the same on the server and
at hydration. In the browser, set that min-height back to 0 right before you mount the field
with loader: 'skeleton'.
Typing the CDN globals
With the script tag and no npm package, wajub, Wajub and WajubError sit on window. To type
them, install @wajub/js as a development dependency and, since your code never imports it, name
it with /// <reference types="@wajub/js" /> at the top of one file or "types": ["@wajub/js"] in
compilerOptions.
declare global {
interface Window {
wajub: WajubSDK;
Wajub: WajubFactory;
WajubError: typeof WajubError;
}
}Framework types
Each framework package exports everything above, and adds its own types.
| Package | Adds |
|---|---|
@wajub/react | WajubProviderProps, CheckoutEmbedProps, ComponentEmbedProps, WajubContextValue |
@wajub/vue | The same four, plus WajubRuntimeRef |
@wajub/svelte | WajubContextValue, WajubRuntimeStore, ComponentEmbedProps, TypedComponentProps and SessionIdSource |
@wajub/angular | WajubOptions, SessionIdSource and WajubConfirmPayment. The inputs and outputs are typed on the component classes, which Angular's template type checking reads |