How to throw/catch custom error with comlink. typescript

Viewed 177

In UI process, I want to catch custom Error throw in Webworker with comlink.

The custom Error is extended Error, so comlink serialize/deserialize to Error not to custom Error.

serialize

if (value instanceof Error) {
  serialized = {
    isError: true,
    value: {
      message: value.message,
      name: value.name,
      stack: value.stack,
    },
  };
}

https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L248

deserialize

if (serialized.isError) {
  throw Object.assign(
    new Error(serialized.value.message),
    serialized.value
  );
}

https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L265

I tried to customize throwTransferHandler to throw custom Error, but It did not work. Because throwMarker is unique Symbol and not exported.

canHandle: (value): value is ThrownValue =>
  isObject(value) && throwMarker in value,

https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L245

1 Answers

I ended up defining local module which reexports comlink and use serialize-error as custom error serialization/deserialization

import * as Comlink from "comlink";
import ErrorCodec from "serialize-error";
export * from "comlink";

interface ThrownValue {
  value: unknown;
}
type SerializedThrownValue =
  | { isError: true; value: ErrorCodec.ErrorObject }
  | { isError: false; value: unknown };

const throwTransferHandler_:
  | Comlink.TransferHandler<unknown, unknown>
  | undefined = Comlink.transferHandlers.get("throw");

const throwTransferHandler = throwTransferHandler_ as Comlink.TransferHandler<
  ThrownValue,
  SerializedThrownValue
>;

const throwTransferHandlerCustom: Comlink.TransferHandler<
  ThrownValue,
  SerializedThrownValue
> = {
  canHandle: throwTransferHandler.canHandle,
  serialize: ({ value }) => {
    let serialized: SerializedThrownValue;
    if (value instanceof Error) {
      serialized = {
        isError: true,
        value: ErrorCodec.serializeError(value),
      };
    } else {
      serialized = { isError: false, value };
    }
    return [serialized, []];
  },
  deserialize: (serialized) => {
    if (serialized.isError) {
      const error = ErrorCodec.deserializeError(serialized.value);
      throw error;
    }
    throw serialized.value;
  },
};

Comlink.transferHandlers.set("throw", throwTransferHandlerCustom);

You should make sure that this version of module is imported in all other places instead of direct comlink import


As an alternative In canHandle you can also use getOwnPropertySymbols and toString:

Object.getOwnPropertySymbols({[Symbol("Comlink.thrown")]: 1})
  .some(_ => _.toString() === 'Symbol(Comlink.thrown)')
Related