import { Observable } from 'rxjs'; /** * The rx-signals {@link Store} uses this type to uniquely identify behaviors representing a root-state behavior. * A `StateId` does not make any use of the generic `T` itself, but is given this * parameter only as a trick to let Typescript infer and thus enforce the correct types. * Use the {@link getStateId} function to generate a corresponding ID. * * @template T - specifies the value-type for the corresponding behavior observable (type of the state) */ export type StateId = symbol & { _rootStateType: T; }; /** * The rx-signals {@link Store} uses this type to uniquely identify behaviors representing a derived-state behavior. * A `DerivedId does not make any use of the generic `T` itself, but is given this * parameter only as a trick to let Typescript infer and thus enforce the correct types. * Use the {@link getDerivedId} function to generate a corresponding ID. * * @template T - specifies the value-type for the corresponding behavior observable */ export type DerivedId = symbol & { _derivedStateType: T; }; /** * The rx-signals {@link Store} uses this type for cases where either a {@link StateId} or a {@link DerivedId} is expected. * * @template T - specifies the value-type for the corresponding behavior observable */ export type BehaviorId = StateId | DerivedId; /** * The rx-signals {@link Store} uses this type to uniquely identify all of its events. * An `EventId` does not make any use of the generic `T` itself, but is given this * parameter only as a trick to let Typescript infer and thus enforce the correct types. * Use the {@link getEventId} function to generate a corresponding ID. * * @template T - specifies the value-type for the corresponding event observable */ export type EventId = symbol & { _eventType: T; }; /** * `SignalId` is the union type of `BehaviorId` and `EventId`, hence it * represents an identifier that corresponds either to a behavior or to an event. * You can use the typeguards {@link isBehaviorId} or {@link isEventId} to check the concrete * type of a `SignalId`. * * @template T - specifies the value-type for the corresponding observable */ export type SignalId = BehaviorId | EventId; /** * `ToSignalId` is a utility type that equals `Signal`, if `S extends SignalId`, else `never`. * * ```ts * ToSignalId> = SignalId * ToSignalId> = SignalId * ToSignalId = never * ``` * * @template S - the generic argument to `ToSignalId` * @template T - the inferred generic parameter of `S`, if `S extends SignalId` */ export type ToSignalId = S extends SignalId ? SignalId : never; /** * `ToBehaviorIdValueType` is a utility type that equals `T`, if `B extends BehaviorId`, else `never`. * * ```ts * ToBehaviorIdValueType> = number * ToBehaviorIdValueType> = never * ``` * * @template B - the generic argument to `ToBehaviorIdValueType` * @template T - the inferred generic parameter of `B`, if `B extends BehaviorId` */ export type ToBehaviorIdValueType = B extends BehaviorId ? T : never; /** * `ToEventIdValueType` is a utility type that equals `T`, if `E extends EventId`, else `never`. * * ```ts * ToEventIdValueType> = number * ToEventIdValueType> = never * ``` * * @template E - the generic argument to `ToEventIdValueType` * @template T - the inferred generic parameter of `E`, if `E extends EventId` */ export type ToEventIdValueType = E extends EventId ? T : never; /** * `ToSignalIdValueType` is a utility type that equals `T`, if `S extends SignalId`, else `never`. * * ```ts * ToSignalIdValueType> = number * ToSignalIdValueType> = string * ToSignalIdValueType = never * ``` * * @template S - the generic argument to `ToSignalIdValueType` * @template T - the inferred generic parameter of `S`, if `S extends SignalId` */ export type ToSignalIdValueType = S extends SignalId ? T : never; /** * `ToObservableValueType` is a utility type that equals `T`, if `O extends Observable`, else `never`. * * ```ts * ToObservableValueType> = number * ToObservableValueType> = string * ToObservableValueType = never * ``` * * @template O - the generic argument to `ToObservableValueType` * @template T - the inferred generic parameter of `O`, if `O extends Observable` */ export type ToObservableValueType = O extends Observable ? T : never; /** * The rx-signals `Store` uses this type to uniquely identify all of its result effects. * An `EffectId` does not make any use of the generic parameters itself, * but is given these parameters only as a trick to let Typescript infer and thus enforce the correct types. * Use the {@link getEffectId} function to generate a corresponding ID. * * @template InputType - specifies the type for the corresponding effects input * @template ResultType - specifies the type for the corresponding effects result * @template ErrorType - specifies the type error-type for the effect. Use never for effects that cannot error. */ export type EffectId = symbol & { _inputType: InputType; _resultType: ResultType; _errorType: ErrorType; }; let stateExtension = 1; let derivedExtension = 1; let eventExtension = 1; let effectExtension = 1; /** * Function to get a new, unique `StateId`. * * @template T - specifies the value-type for the corresponding behavior * @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually, you don't need this, cause even for debugging purposes, you should use {@link Store.setIdName} and {@link Store.getIdName}. * @returns {StateId} */ export const getStateId = (nameExtension?: string): StateId => Symbol(`S_${(nameExtension ?? '') + stateExtension++}`) as StateId; /** * Function to get a new, unique `DerivedId`. * * @template T - specifies the value-type for the corresponding behavior * @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually, you don't need this, cause even for debugging purposes, you should use {@link Store.setIdName} and {@link Store.getIdName}. * @returns {DerivedId} */ export const getDerivedId = (nameExtension?: string): DerivedId => Symbol(`D_${(nameExtension ?? '') + derivedExtension++}`) as DerivedId; /** * Function to get a new, unique `EventId`. * * @template T - specifies the value-type for the corresponding event * @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually, you don't need this, cause even for debugging purposes, you should use {@link Store.setIdName} and {@link Store.getIdName}. * @returns {EventId} */ export const getEventId = (nameExtension?: string): EventId => Symbol(`E_${(nameExtension ?? '') + eventExtension++}`) as EventId; /** * Function to get a new, unique `EffectId`. * * @template InputType - specifies the type for the corresponding effects input * @template ResultType - specifies the type for the corresponding effects result * @template ErrorType - specifies the type error-type for the effect. Use `never` for effects that cannot error. * @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually you should not need this. * @returns {EventId} */ export const getEffectId = ( nameExtension?: string, ): EffectId => Symbol(`Effect_${(nameExtension ?? '') + effectExtension++}`) as EffectId< InputType, ResultType, ErrorType >; /** * Typeguard to check whether a given `SignalId` is a `StateId`. * * @template T - specifies the type for the corresponding signal * @param {Signal} id - a signal identifier. * @returns {boolean} */ export const isStateId = (id: SignalId): id is StateId => id.toString().startsWith('Symbol(S'); /** * Typeguard to check whether a given `SignalId` is a `DerivedId`. * * @template T - specifies the type for the corresponding signal * @param {Signal} id - a signal identifier. * @returns {boolean} */ export const isDerivedId = (id: SignalId): id is DerivedId => id.toString().startsWith('Symbol(D'); /** * Typeguard to check whether a given `SignalId` is a `BehaviorId`. * * @template T - specifies the type for the corresponding signal * @param {Signal} id - a signal identifier. * @returns {boolean} */ export const isBehaviorId = (id: SignalId): id is BehaviorId => isStateId(id) || isDerivedId(id); /** * Typeguard to check whether a given `SignalId` is an `EventId`. * * @template T - specifies the type for the corresponding signal * @param {Signal} id - a signal identifier. * @returns {boolean} */ export const isEventId = (id: SignalId): id is EventId => id.toString().startsWith('Symbol(E'); /** * A constant representing the intentional absence of a value. * Even undefined and null are valid values, so these cannot be used to represent no-value. */ export const NO_VALUE: '$RX-SIGNALS-NO-VALUE$' = '$RX-SIGNALS-NO-VALUE$'; /** * The corresponding type for {@link NO_VALUE} */ export type NoValueType = typeof NO_VALUE; /** * Typeguard to check if a value is NOT {@link NO_VALUE} */ export const isNotNoValueType = (v: T): v is Exclude => v !== NO_VALUE; /** * Typeguard to check if a value is {@link NO_VALUE} */ export const isNoValueType = (v: any): v is NoValueType => v === NO_VALUE;