Repository: remix-run/react-router
File: packages/router/utils.ts (TypeScript)

Code before:
    if (this.done) {
      // Nothing left to abort!
      this.unlistenAbortSignal();
    }

    if (error) {
      Object.defineProperty(promise, "_error", { get: () => error });
      this.emit(false, key);
      return Promise.reject(error);
    }

    // TODO: We oughta just do the same for `error`.  If so we should add an
    // `isError` boolean to this function so we stop using the presence of
    // error/data as the switch

    // Option 1
    if (data === undefined) {
      Object.defineProperty(promise, "_error", {
        get: () =>
          new Error(
            `Deferred data for key ${key} resolved to \`undefined\`, you must resolve with a value or \`null\`.`
          ),
      });
      this.emit(false, key);
      return Promise.reject(error);
    }

    // Option 2
    if (data === undefined) {
      console.warn(
        `Deferred data for key ${key} resolved to \`undefined\`, defaulting to \`null\`.`
      );
      data = null;
    }

    Object.defineProperty(promise, "_data", { get: () => data });
    this.emit(false, key);
    return data;
  }

  private emit(aborted: boolean, settledKey?: string) {
    this.subscribers.forEach((subscriber) => subscriber(aborted, settledKey));
  }

  subscribe(fn: (aborted: boolean, settledKey?: string) => void) {
    this.subscribers.add(fn);
    return () => this.subscribers.delete(fn);
  }

  cancel() {
    this.controller.abort();

Review comment (suggestion):
This approach better aligns with our handling of `function loader() { return undefined; }`

Code after:
    if (this.done) {
      // Nothing left to abort!
      this.unlistenAbortSignal();
    }

    // If the promise was resolved/rejected with undefined, we'll throw an error as you
    // should always resolve with a value or null
    if (error === undefined && data === undefined) {
      let undefinedError = new Error(
        `Deferred data for key "${key}" resolved/rejected with \`undefined\`, ` +
          `you must resolve/reject with a value or \`null\`.`
      );
      Object.defineProperty(promise, "_error", { get: () => undefinedError });
      this.emit(false, key);
      return Promise.reject(undefinedError);
    }

    if (data === undefined) {
      Object.defineProperty(promise, "_error", { get: () => error });
      this.emit(false, key);
      return Promise.reject(error);
    }

    Object.defineProperty(promise, "_data", { get: () => data });
    this.emit(false, key);
    return data;
  }

  private emit(aborted: boolean, settledKey?: string) {
    this.subscribers.forEach((subscriber) => subscriber(aborted, settledKey));
  }

  subscribe(fn: (aborted: boolean, settledKey?: string) => void) {
    this.subscribers.add(fn);
    return () => this.subscribers.delete(fn);
  }

  cancel() {
    this.controller.abort();
    this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));
    this.emit(true);
  }

  async resolveData(signal: AbortSignal) {
    let aborted = false;
    if (!this.done) {
      let onAbort = () => this.cancel();
      signal.addEventListener("abort", onAbort);
      aborted = await new Promise((resolve) => {
        this.subscribe((aborted) => {
          signal.removeEventListener("abort", onAbort);