chromium/ash/webui/recorder_app_ui/resources/core/reactive/signal/types.ts

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview This file contains all public type interface of signal.
 *
 * Note that all types are re-exported by signal.ts, and user outside of the
 * signal implementation should import from that file instead.
 */

import {produce} from '../../utils/draft.js';

export abstract class ReadonlySignal<T> {
  /**
   * Gets the value of the signal.
   *
   * When used in computed or effect, the signal will be added as a dependency
   * of the calling computed / effect.
   */
  abstract get value(): T;

  /**
   * Gets the number of children.
   *
   * This is for unit test only.
   */
  abstract numChildrenForTesting(): number;

  /**
   * Gets the value without tracking it as dependency.
   */
  abstract peek(): T;
  // TODO(pihsun): subscribe(), ...
}

export abstract class Signal<T> extends ReadonlySignal<T> {
  /**
   * Sets the value of the signal.
   */
  abstract override set value(newValue: T);

  /**
   * Updates the signal value based on the old value.
   *
   * This is a shortcut of `this.value = updater(this.value);`.
   *
   * Note that the updater should returns a new value without updating the
   * value in place, since current implementation relies on object identity for
   * change detection.
   */
  update(updater: (val: T) => T): void {
    this.value = updater(this.peek());
  }

  /**
   * "Mutates" the signal value.
   *
   * This is a shortcut of `this.value = produce(this.value, recipe);`.
   *
   * Note that the value isn't actually mutated in place but immutably updated
   * via draft.ts.
   */
  mutate(recipe: (draft: T) => void): void {
    this.value = produce(this.peek(), recipe);
  }

  // TODO(pihsun): other needed functions.
}

export type Computed<T> = ReadonlySignal<T>;

export type Dispose = () => void;