Skip to content

Safe vs unsafe functions

Most of our code examples call functions named xxxUnsafe(), but don’t be alarmed by the name! Unsafe indicates that the function could throw one or more errors. To determine which errors can be thrown please check the documentation or the jsDoc.

The downside to throwing errors is that they escape Typescript’s type checking. You need to read the jsDoc and explicitly handle potential errors. As an alternative we offer safe variants of most functions e.g. registerPasskey(). These functions return a tagged, discriminated union that encompasses both the successful response along with the potential errors e.g.

interface PasskeysUnsupportedError {
_tag: "PasskeysUnsupportedError";
}
interface DuplicatePasskeyError {
_tag: "DuplicatePasskeyError";
}
interface RegistrationResponse {
_tag: "RegistrationResponse";
idToken: string;
code: string;
}
type registerPasskey =
(options: RegistrationOptions) =>
Promise<RegistrationResponse | RegistrationError | NetworkError>;

You can then use the _tag discriminator to handle the different outcomes:

const result = await registerPasskey(...);
switch (result._tag) {
case "PasskeysUnsupportedError":
console.log("Passkeys not supported on this device");
break;
case "DuplicatePasskeyError":
console.log("Passkey already registered on this device");
break;
case "RegistrationResponse":
console.log("Happy days!");
break;
default:
break;
}

The advantage to this approach is that you’ll get a TSC error if you try to work with the “happy” result, without explicitly testing in a type guard.