Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw/catch custom error with comlink. typescript

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

like image 532
Tetsuo Kitamura Avatar asked Mar 16 '26 05:03

Tetsuo Kitamura


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)')
like image 167
Safareli Avatar answered Mar 19 '26 00:03

Safareli