import { computed } from "mobx" import { Model } from "../model/Model" import { modelTypeKey } from "../model/metadata" import type { ModelClass } from "../modelShared/BaseModelShared" import { typesString } from "../types/primitiveBased/typesPrimitive" import { tProp } from "../types/tProp" import { failure } from "../utils" /** * A reference model base type. * Use `customRef` to create a custom ref constructor. */ export abstract class Ref extends Model({ /** * Reference id. */ id: tProp(typesString), }) { protected abstract resolve(): T | undefined /** * The object this reference points to, or `undefined` if the reference is currently invalid. */ @computed get maybeCurrent(): T | undefined { return this.resolve() } /** * If the reference is currently valid. */ @computed get isValid(): boolean { return !!this.maybeCurrent } /** * The object this reference points to, or throws if invalid. */ @computed get current(): T { const current = this.maybeCurrent if (!current) { throw failure( `a reference of type '${this[modelTypeKey]}' could not resolve an object with id '${this.id}'` ) } return current } /** * Ensures back references for this ref are up to date. * This only needs to be called if you need to get the most up to date * back references while both still inside an action and while the reference * is not a child of the same root than the target. */ abstract forceUpdateBackRefs(): void } /** * @ignore */ export declare const customRefTypeSymbol: unique symbol /** A ref constructor for custom refs */ export interface RefConstructor { (valueOrID: TE | string): Ref refClass: ModelClass> [customRefTypeSymbol]: T // just for typings } /** * Checks if a ref object is of a given ref type. * * @template T Referenced object type. * @param ref Reference object. * @param refType Reference type. * @returns `true` if it is of the given type, false otherwise. */ export function isRefOfType( ref: Ref, refType: RefConstructor ): ref is Ref { return ref instanceof refType.refClass }