Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #3167 (2025-02-15 21:50:58)

Types and tests for the non-layer files in `client/pixi/layers/control/`
Ruler, DoorControl, and Cursor


Patch:
diff --git a/src/foundry/client/config.d.mts b/src/foundry/client/config.d.mts
index be88ec712..3a02c3a2c 100644
--- a/src/foundry/client/config.d.mts
+++ b/src/foundry/client/config.d.mts
@@ -945,8 +945,9 @@ declare global {
 
       /**
        * The class used to render door control icons
+       * @remarks Not `AnyConstructor` because it's instantiated with a `Wall.Implementation` as its first argument
        */
-      doorControlClass: DoorControl.AnyConstructor;
+      doorControlClass: typeof DoorControl;
 
       /** @defaultValue `0x000000` */
       exploredColor: number;
@@ -1009,8 +1010,11 @@ declare global {
 
       visualEffectsMaskingFilter: VisualEffectsMaskingFilter.AnyConstructor;
 
-      /** @defaultValue `Ruler` */
-      rulerClass: Ruler.AnyConstructor;
+      /**
+       * @defaultValue `Ruler`
+       * @remarks Not `AnyConstructor` because it's instantiated with a `User.Implementation` as its first argument
+       */
+      rulerClass: typeof Ruler;
 
       /** @defaultValue `0.8` */
       dragSpeedModifier: number;
diff --git a/src/foundry/client/data/documents/token.d.mts b/src/foundry/client/data/documents/token.d.mts
index 18891601a..39a54e333 100644
--- a/src/foundry/client/data/documents/token.d.mts
+++ b/src/foundry/client/data/documents/token.d.mts
@@ -1,4 +1,4 @@
-import type { DeepPartial, InexactPartial } from "fvtt-types/utils";
+import type { AnyObject, DeepPartial, InexactPartial } from "fvtt-types/utils";
 import type Document from "../../../common/abstract/document.d.mts";
 import type { SchemaField } from "../../../common/data/fields.d.mts";
 import type BaseToken from "../../../common/documents/token.d.mts";
@@ -25,6 +25,8 @@ declare global {
           _priorPosition: Record<string, { x: number; y: number; elevation: number }>;
           teleport: boolean;
           forced: boolean;
+          // TODO: Type this accurately when going over the Token placeable
+          animation: AnyObject;
         },
         {}
       > {}
diff --git a/src/foundry/client/pixi/layers/controls/cursor.d.mts b/src/foundry/client/pixi/layers/controls/cursor.d.mts
index 1bdb3e203..aa9872761 100644
--- a/src/foundry/client/pixi/layers/controls/cursor.d.mts
+++ b/src/foundry/client/pixi/layers/controls/cursor.d.mts
@@ -8,15 +8,15 @@ declare global {
     constructor(user: User.ConfiguredInstance);
 
     /**
-     * Update visibility and animations
-     * @param user - The user
+     * @defaultValue `{x: 0, y: 0}`
      */
-    refreshVisibility(user: User.ConfiguredInstance): void;
+    target: PIXI.IPointData;
 
     /**
-     * @defaultValue `{x: 0, y: 0}`
+     * Update visibility and animations
+     * @param user - The user
      */
-    target: { x: number; y: number };
+    refreshVisibility(user: User.ConfiguredInstance): void;
 
     /**
      * Draw the user's cursor as a small dot with their user name attached as text
@@ -30,4 +30,13 @@ declare global {
 
     override destroy(options?: PIXI.IDestroyOptions | boolean): void;
   }
+
+  namespace Cursor {
+    interface Any extends AnyCursor {}
+    type AnyConstructor = typeof AnyCursor;
+  }
+}
+
+declare abstract class AnyCursor extends Cursor {
+  constructor(arg0: never, ...args: never[]);
 }
diff --git a/src/foundry/client/pixi/layers/controls/door.d.mts b/src/foundry/client/pixi/layers/controls/door.d.mts
index 6e237755f..da5fc2db8 100644
--- a/src/foundry/client/pixi/layers/controls/door.d.mts
+++ b/src/foundry/client/pixi/layers/controls/door.d.mts
@@ -1,4 +1,4 @@
-export {};
+import type { FixedInstanceType } from "../../../../../utils/index.d.mts";
 
 declare global {
   /**
@@ -9,6 +9,12 @@ declare global {
 
     wall: Wall.ConfiguredInstance;
 
+    /**
+     * @defaultValue `false`
+     * @remarks "Door controls are not visible by default"
+     */
+    override visible: boolean;
+
     /**
      * The center of the wall which contains the door.
      */
@@ -19,16 +25,19 @@ declare global {
      */
     draw(): Promise<this>;
 
-    icon?: PIXI.Sprite;
-
+    /** @remarks Not initialized or even defined until set in {@link DoorControl#draw} */
     bg?: PIXI.Graphics;
 
+    /** @remarks Not initialized or even defined until set in {@link DoorControl#draw} */
+    icon?: PIXI.Sprite;
+
+    /** @remarks Not initialized or even defined until set in {@link DoorControl#draw} */
     border?: PIXI.Graphics;
 
     /**
      * Get the icon texture to use for the Door Control icon based on the door state
      */
-    protected _getTexture(): ReturnType<typeof getTexture>;
+    protected _getTexture(): LoadTexture.Return;
 
     reposition(): void;
 
@@ -43,32 +52,35 @@ declare global {
      * Handle mouse over events on a door control icon.
      * @param event - The originating interaction event
      */
-    protected _onMouseOver(event: PIXI.FederatedEvent): false | undefined;
+    protected _onMouseOver(event: PIXI.FederatedEvent): false | void;
 
     /**
      * Handle mouse out events on a door control icon.
      * @param event - The originating interaction event
      */
-    protected _onMouseOut(event: PIXI.FederatedEvent): false | undefined;
+    protected _onMouseOut(event: PIXI.FederatedEvent): false | void;
 
     /**
      * Handle left mouse down events on a door control icon.
      * This should only toggle between the OPEN and CLOSED states.
      * @param event - The originating interaction event
      */
-    protected _onMouseDown(event: PIXI.FederatedEvent): false | undefined | Promise<WallDocument.ConfiguredInstance>;
+    protected _onMouseDown(event: PIXI.FederatedEvent): false | void | Promise<WallDocument.ConfiguredInstance>;
 
     /**
      * Handle right mouse down events on the door control icon
      * This should toggle whether the door is LOCKED or CLOSED
      * @param event - The originating interaction event
      */
-    protected _onRightDown(event: PIXI.FederatedEvent): undefined | Promise<WallDocument.ConfiguredInstance>;
+    protected _onRightDown(event: PIXI.FederatedEvent): void | Promise<WallDocument.ConfiguredInstance>;
   }
 
   namespace DoorControl {
-    type Any = AnyDoorControl;
+    interface Any extends AnyDoorControl {}
     type AnyConstructor = typeof AnyDoorControl;
+
+    type ConfiguredClass = CONFIG["Canvas"]["doorControlClass"];
+    type ConfiguredInstance = FixedInstanceType<ConfiguredClass>;
   }
 }
 
diff --git a/src/foundry/client/pixi/layers/controls/ruler.d.mts b/src/foundry/client/pixi/layers/controls/ruler.d.mts
index 20e52e00e..d2f666552 100644
--- a/src/foundry/client/pixi/layers/controls/ruler.d.mts
+++ b/src/foundry/client/pixi/layers/controls/ruler.d.mts
@@ -1,4 +1,5 @@
-import type { NullishProps, ValueOf } from "fvtt-types/utils";
+import type { Brand, FixedInstanceType, IntentionalPartial, NullishProps } from "fvtt-types/utils";
+import type Document from "../../../../common/abstract/document.d.mts";
 
 declare global {
   /**
@@ -10,15 +11,7 @@ declare global {
      *                  (default: `game.user`)
      * @param options - Additional options
      */
-    constructor(
-      user?: User.ConfiguredInstance,
-      options?: NullishProps<{
-        /**
-         * The color of the ruler (defaults to the color of the User)
-         */
-        color?: number;
-      }>,
-    );
+    constructor(user?: User.ConfiguredInstance, options?: Ruler.ConstructorOptions);
 
     /**
      * Record the User which this Ruler references
@@ -34,7 +27,7 @@ declare global {
 
     /**
      * The ruler color - by default the color of the active user
-     * @defaultValue `foundry.utils.colorStringToHex(this.user.data.color) || 0x42F4E2`
+     * @defaultValue `this.user.color`
      */
     color: Color;
 
@@ -51,7 +44,7 @@ declare global {
     /**
      * The possible Ruler measurement states.
      */
-    static get STATES(): Ruler.STATES;
+    static get STATES(): Ruler.States;
 
     /**
      * Is the ruler ready for measure?
@@ -101,15 +94,15 @@ declare global {
     totalCost: number;
 
     /**
-     * The current state of the Ruler (one of {@link Ruler.STATES}).
+     * The current state of the Ruler (one of {@link Ruler.States}).
      */
-    get state(): ValueOf<Ruler.STATES>;
+    get state(): Ruler.STATES;
 
     /**
-     * The current state of the Ruler (one of {@link Ruler.STATES}).
+     * The current state of the Ruler (one of {@link Ruler.States}).
      * @defaultValue `Ruler.STATES.INACTIVE`
      */
-    protected _state: ValueOf<Ruler.STATES>;
+    protected _state: Ruler.STATES;
 
     /**
      * Is the Ruler being actively used to measure distance?
@@ -139,19 +132,7 @@ declare global {
      */
     measure(
       destination: Canvas.Point,
-      options?: NullishProps<{
-        /**
-         * Snap the destination?
-         * @defaultValue `true`
-         */
-        snap: boolean;
-
-        /**
-         * If not forced and the destination matches the current destination of this ruler, no measuring is done and nothing is returned
-         * @defaultValue `false`
-         */
-        force: boolean;
-      }>,
+      options?: Ruler.MeasureOptions, // not:null (destructured)
     ): Ruler.MeasurementSegment[] | void;
 
     /**
@@ -161,13 +142,7 @@ declare global {
      */
     protected _getMeasurementOrigin(
       point: Canvas.Point,
-      options?: NullishProps<{
-        /**
-         * Snap the waypoint?
-         * @defaultValue `true`
-         */
-        snap: boolean;
-      }>,
+      options?: Ruler.GetMeasurementOriginOptions, // not:null (destructured)
     ): Canvas.Point;
 
     /**
@@ -178,13 +153,7 @@ declare global {
      */
     protected _getMeasurementDestination(
       point: Canvas.Point,
-      options?: NullishProps<{
-        /**
-         * Snap the point?
-         * @defaultValue `true`
-         */
-        snap: boolean;
-      }>,
+      options?: Ruler.GetMeasurementDestinationOptions, // not:null (destructured)
     ): Canvas.Point;
 
     /**
@@ -200,18 +169,7 @@ declare global {
      */
     protected _startMeasurement(
       origin: Canvas.Point,
-      options?: NullishProps<{
-        /**
-         * Snap the origin?
-         * @defaultValue `true`
-         */
-        snap: boolean;
-
-        /**
-         * The token that is moved (defaults to {@link Ruler#_getMovementToken})
-         */
-        token: Token.ConfiguredInstance | null;
-      }>,
+      options?: Ruler.StartMeasurementOptions, // not:null (destructured)
     ): void;
 
     /**
@@ -226,13 +184,7 @@ declare global {
      */
     protected _addWaypoint(
       point: Canvas.Point,
-      options?: NullishProps<{
-        /**
-         * Snap the waypoint?
-         * @defaultValue `true`
-         */
-        snap: boolean;
-      }>,
+      options?: Ruler.AddWaypointOptions, // not:null (destructured)
     ): void;
 
     /**
@@ -242,6 +194,7 @@ declare global {
 
     /**
      * Get the cost function to be used for Ruler measurements.
+     * @remarks Just a stub in v12.331
      */
     protected _getCostFunction(): foundry.grid.BaseGrid.MeasurePathCostFunction | void;
 
@@ -253,7 +206,7 @@ declare global {
     /**
      * Get the text label for a segment of the measured path
      */
-    protected _getSegmentLabel(segment: Ruler.MeasurementSegment): string;
+    protected _getSegmentLabel(segment: Ruler.PartialSegmentForLabelling): string;
 
     /**
      * Draw each segment of the measured path.
@@ -263,7 +216,7 @@ declare global {
     /**
      * Highlight the measurement required to complete the move in the minimum number of discrete spaces
      */
-    protected _highlightMeasurementSegment(segment: Ruler.MeasurementSegment): void;
+    protected _highlightMeasurementSegment(segment: Ruler.PartialSegmentForHighlighting): void;
 
     /**
      * Determine whether a SPACE keypress event entails a legal token movement along a measured ruler
@@ -284,6 +237,7 @@ declare global {
     /**
      * Get the current measurement history.
      * @returns The current measurement history, if any
+     * @remarks Just a stub in v12.331
      */
     protected _getMeasurementHistory(): Ruler.MeasurementHistory | void;
 
@@ -318,22 +272,22 @@ declare global {
      */
     protected _animateSegment(
       token: Token.ConfiguredInstance,
-      segment: Ruler.MeasurementSegment,
+      segment: Ruler.PartialSegmentForAnimating,
       destination: Canvas.Point,
-      updateOptions: TokenDocument.DatabaseOperations["update"],
+      updateOptions?: Ruler.PartialTokenUpdateOptions, // not:null (used as first param in mergeObject)
     ): Promise<void>;
 
     /**
      * An method which can be extended by a subclass of Ruler to define custom behaviors before a confirmed movement.
      * @param token - The Token that will be moving
      */
-    protected _preMove(token: Token): Promise<void>;
+    protected _preMove(token: Token.ConfiguredInstance): Promise<void>;
 
     /**
      * An event which can be extended by a subclass of Ruler to define custom behaviors before a confirmed movement.
      * @param token - The Token that finished moving
      */
-    protected _postMove(token: Token): Promise<void>;
+    protected _postMove(token: Token.ConfiguredInstance): Promise<void>;
 
     /**
      * Broadcast Ruler measurement if its User is the connected client.
@@ -350,7 +304,7 @@ declare global {
      * Update a Ruler instance using data provided through the cursor activity socket
      * @param data - Ruler data with which to update the display
      */
-    update(data: Ruler.MeasurementData | null): void;
+    update(data?: Ruler.MeasurementData | null): void;
 
     /**
      * Handle the beginning of a new Ruler measurement workflow
@@ -389,24 +343,44 @@ declare global {
 
     /**
      * Move the Token along the measured path when the move key is pressed.
+     * @remarks `context` is unused in 12.331
      */
     protected _onMoveKeyDown(context: KeyboardManager.KeyboardEventContext): void;
   }
 
   namespace Ruler {
+    interface Any extends AnyRuler {}
     type AnyConstructor = typeof AnyRuler;
 
-    interface STATES {
-      readonly INACTIVE: 0;
-      readonly STARTING: 1;
-      readonly MEASURING: 2;
-      readonly MOVING: 3;
+    type ConfiguredClass = CONFIG["Canvas"]["rulerClass"];
+    type ConfiguredInstance = FixedInstanceType<ConfiguredClass>;
+
+    /** @internal */
+    type _ConstructorOptions = NullishProps<{
+      /**
+       * The color of the ruler (defaults to the color of the User)
+       * @defaultValue `this.user.color`
+       */
+      color?: number;
+    }>;
+
+    interface ConstructorOptions extends _ConstructorOptions {}
+
+    type STATES = Brand<number, "Ruler.STATES">;
+
+    interface States {
+      readonly INACTIVE: 0 & STATES;
+      readonly STARTING: 1 & STATES;
+      readonly MEASURING: 2 & STATES;
+      readonly MOVING: 3 & STATES;
     }
 
     interface MeasurementSegment {
       /** The Ray which represents the point-to-point line segment */
       ray: Ray;
 
+      teleport: boolean;
+
       /** The text object used to display a label for this segment */
       label: PreciseText;
 
@@ -432,7 +406,7 @@ declare global {
       last: boolean;
 
       /** Animation options passed to {@link TokenDocument#update} */
-      animation: object;
+      animation: Document.Database.OperationOf<"Token", "update">["animation"];
     }
 
     interface MeasurementHistoryWaypoint {
@@ -453,7 +427,7 @@ declare global {
 
     interface MeasurementData {
       /** The state ({@link Ruler#state}) */
-      state: ValueOf<Ruler.STATES>;
+      state: Ruler.STATES;
 
       /** The token ID ({@link Ruler#token}) */
       token: string | null;
@@ -467,6 +441,58 @@ declare global {
       /** The destination ({@link Ruler#destination}) */
       destination: Canvas.Point | null;
     }
+
+    /** @internal */
+    type _Snap = NullishProps<{
+      /**
+       * Snap the destination?
+       * @defaultValue `true`
+       */
+      snap: boolean;
+    }>;
+
+    /** @internal */
+    type _MeasureOptions = NullishProps<{
+      /**
+       * If not forced and the destination matches the current destination of this ruler, no measuring is done and nothing is returned
+       * @defaultValue `false`
+       */
+      force: boolean;
+    }>;
+
+    interface MeasureOptions extends _Snap, _MeasureOptions {}
+
+    interface GetMeasurementOriginOptions extends _Snap {}
+
+    interface GetMeasurementDestinationOptions extends _Snap {}
+
+    /** @internal */
+    type _StartMeasurementOptions = NullishProps<{
+      /**
+       * The token that is moved (defaults to {@link Ruler#_getMovementToken})
+       */
+      token: Token.ConfiguredInstance | null;
+    }>;
+
+    interface StartMeasurementOptions extends _StartMeasurementOptions, _Snap {}
+
+    interface AddWaypointOptions extends _Snap {}
+
+    // TODO: revisit after docs v2
+    type PartialTokenUpdateOptions = IntentionalPartial<Document.Database.OperationOf<"Token", "update">>;
+
+    // TODO: also revisit after docs v2 merges with new and improved IntentionalPartial
+    interface PartialSegmentForLabelling
+      extends Pick<MeasurementSegment, "teleport" | "last" | "distance">,
+        IntentionalPartial<Omit<MeasurementSegment, "teleport" | "last" | "distance">> {}
+
+    interface PartialSegmentForHighlighting
+      extends Pick<MeasurementSegment, "teleport" | "ray">,
+        IntentionalPartial<Omit<MeasurementSegment, "teleport" | "ray">> {}
+
+    interface PartialSegmentForAnimating
+      extends Pick<MeasurementSegment, "teleport" | "animation">,
+        IntentionalPartial<Omit<MeasurementSegment, "teleport" | "animation">> {}
   }
 }
 


Test patch:
diff --git a/tests/foundry/client/pixi/layers/controls/cursor.test-d.ts b/tests/foundry/client/pixi/layers/controls/cursor.test-d.ts
index 0a33a5549..9f405d41a 100644
--- a/tests/foundry/client/pixi/layers/controls/cursor.test-d.ts
+++ b/tests/foundry/client/pixi/layers/controls/cursor.test-d.ts
@@ -1,8 +1,11 @@
 import { expectTypeOf } from "vitest";
 
-declare const user: User;
+declare const user: User.ConfiguredInstance;
 
 const cursor = new Cursor(user);
+
+expectTypeOf(cursor.target).toEqualTypeOf<PIXI.IPointData>();
+expectTypeOf(cursor.refreshVisibility(user)).toBeVoid();
 expectTypeOf(cursor.draw(user)).toEqualTypeOf<void>();
 expectTypeOf(cursor.destroy()).toEqualTypeOf<void>();
 expectTypeOf(cursor.destroy({})).toEqualTypeOf<void>();
diff --git a/tests/foundry/client/pixi/layers/controls/door.test-d.ts b/tests/foundry/client/pixi/layers/controls/door.test-d.ts
index 1b7b9b4c4..440e41219 100644
--- a/tests/foundry/client/pixi/layers/controls/door.test-d.ts
+++ b/tests/foundry/client/pixi/layers/controls/door.test-d.ts
@@ -4,12 +4,26 @@ declare const wall: Wall;
 
 // @ts-expect-error - A DoorControl requires a wall.
 new DoorControl();
-
 const control = new DoorControl(wall);
-expectTypeOf(control.wall).toEqualTypeOf<Wall>();
-expectTypeOf(control.draw()).toEqualTypeOf<Promise<DoorControl>>();
+
+expectTypeOf(control.wall).toEqualTypeOf<Wall.ConfiguredInstance>();
+expectTypeOf(control.center).toEqualTypeOf<PIXI.Point>();
+expectTypeOf(control.isVisible).toEqualTypeOf<boolean>();
+
+expectTypeOf(control.draw()).toEqualTypeOf<Promise<DoorControl.ConfiguredInstance>>();
+
 expectTypeOf(control.bg).toEqualTypeOf<PIXI.Graphics | undefined>();
 expectTypeOf(control.icon).toEqualTypeOf<PIXI.Sprite | undefined>();
 expectTypeOf(control.border).toEqualTypeOf<PIXI.Graphics | undefined>();
+
+expectTypeOf(control["_getTexture"]()).toEqualTypeOf<LoadTexture.Return>();
+
 expectTypeOf(control.reposition()).toEqualTypeOf<void>();
-expectTypeOf(control.isVisible).toEqualTypeOf<boolean>();
+
+declare const someEvent: PIXI.FederatedEvent;
+expectTypeOf(control["_onMouseOver"](someEvent)).toEqualTypeOf<false | void>();
+expectTypeOf(control["_onMouseOut"](someEvent)).toEqualTypeOf<false | void>();
+expectTypeOf(control["_onMouseDown"](someEvent)).toEqualTypeOf<
+  Promise<WallDocument.ConfiguredInstance> | false | void
+>();
+expectTypeOf(control["_onRightDown"](someEvent)).toEqualTypeOf<Promise<WallDocument.ConfiguredInstance> | void>();
diff --git a/tests/foundry/client/pixi/layers/controls/ruler.test-d.ts b/tests/foundry/client/pixi/layers/controls/ruler.test-d.ts
index 1e080833c..6c9c0d64a 100644
--- a/tests/foundry/client/pixi/layers/controls/ruler.test-d.ts
+++ b/tests/foundry/client/pixi/layers/controls/ruler.test-d.ts
@@ -1,28 +1,125 @@
 import { expectTypeOf } from "vitest";
-import Color = foundry.utils.Color;
 
-if (game instanceof Game) {
+expectTypeOf(Ruler.STATES).toMatchTypeOf<Record<keyof Ruler.States, Ruler.STATES>>();
+expectTypeOf(Ruler.canMeasure).toBeBoolean();
+
+if (game.ready) {
   expectTypeOf(new Ruler(undefined)).toEqualTypeOf<Ruler>();
   expectTypeOf(new Ruler(game.user, { color: null })).toEqualTypeOf<Ruler>();
   expectTypeOf(new Ruler(game.user, { color: 0x32f4e2 })).toEqualTypeOf<Ruler>();
 }
 
 const ruler = new Ruler();
-expectTypeOf(ruler.user).toEqualTypeOf<User>();
+declare const somePoint: PIXI.Point;
+
+expectTypeOf(ruler.user).toEqualTypeOf<User.ConfiguredInstance>();
 expectTypeOf(ruler.name).toEqualTypeOf<string>();
 expectTypeOf(ruler.color).toEqualTypeOf<Color>();
-expectTypeOf(ruler.waypoints).toEqualTypeOf<PIXI.Point[]>();
-expectTypeOf(ruler.destination).toEqualTypeOf<PIXI.Point | null>();
+
 expectTypeOf(ruler.ruler).toEqualTypeOf<PIXI.Graphics>();
 expectTypeOf(ruler.labels).toEqualTypeOf<PIXI.Container>();
-expectTypeOf(Ruler.STATES).toEqualTypeOf<{ INACTIVE: 0; STARTING: 1; MEASURING: 2; MOVING: 3 }>();
+
+expectTypeOf(ruler.destination).toEqualTypeOf<Canvas.Point | null>();
+expectTypeOf(ruler.origin).toEqualTypeOf<Canvas.Point | null>();
+expectTypeOf(ruler.waypoints).toEqualTypeOf<Canvas.Point[]>();
+
+expectTypeOf(ruler.segments).toEqualTypeOf<Ruler.MeasurementSegment[]>();
+expectTypeOf(ruler.history).toEqualTypeOf<Ruler.MeasurementHistoryWaypoint[]>();
+
+expectTypeOf(ruler.totalDistance).toBeNumber();
+expectTypeOf(ruler.totalCost).toBeNumber();
+
+expectTypeOf(ruler.state).toEqualTypeOf<Ruler.STATES>();
+expectTypeOf(ruler["_state"]).toEqualTypeOf<Ruler.STATES>();
+
 expectTypeOf(ruler.active).toEqualTypeOf<boolean>();
+expectTypeOf(ruler.highlightLayer).toEqualTypeOf<GridHighlight>();
+expectTypeOf(ruler.token).toEqualTypeOf<Token.ConfiguredInstance | null>();
+
+expectTypeOf(ruler.clear()).toBeVoid();
+
+expectTypeOf(ruler.measure(somePoint)).toEqualTypeOf<Ruler.MeasurementSegment[] | void>();
+expectTypeOf(ruler.measure({ x: 10, y: 10 }, {})).toEqualTypeOf<Ruler.MeasurementSegment[] | void>();
+expectTypeOf(ruler.measure({ x: 10, y: 10 }, { snap: null, force: undefined })).toEqualTypeOf<
+  Ruler.MeasurementSegment[] | void
+>();
+
+expectTypeOf(ruler["_getMeasurementOrigin"](somePoint)).toEqualTypeOf<Canvas.Point>();
+expectTypeOf(ruler["_getMeasurementOrigin"](somePoint, {})).toEqualTypeOf<Canvas.Point>();
+expectTypeOf(ruler["_getMeasurementOrigin"](somePoint, { snap: false })).toEqualTypeOf<Canvas.Point>();
+
+expectTypeOf(ruler["_getMeasurementDestination"](somePoint)).toEqualTypeOf<Canvas.Point>();
+expectTypeOf(ruler["_getMeasurementDestination"](somePoint, {})).toEqualTypeOf<Canvas.Point>();
+expectTypeOf(ruler["_getMeasurementDestination"](somePoint, { snap: undefined })).toEqualTypeOf<Canvas.Point>();
+
+expectTypeOf(ruler["_getMeasurementSegments"]()).toEqualTypeOf<Ruler.MeasurementSegment[]>();
+
+expectTypeOf(ruler["_startMeasurement"](somePoint)).toBeVoid();
+expectTypeOf(ruler["_startMeasurement"](somePoint, {})).toBeVoid();
+expectTypeOf(ruler["_startMeasurement"](somePoint, { snap: true })).toBeVoid();
+
+expectTypeOf(ruler["_endMeasurement"]()).toBeVoid();
+
+expectTypeOf(ruler["_addWaypoint"](somePoint)).toBeVoid();
+expectTypeOf(ruler["_addWaypoint"](somePoint, {})).toBeVoid();
+expectTypeOf(ruler["_addWaypoint"](somePoint, { snap: null })).toBeVoid();
+
+expectTypeOf(ruler["_getCostFunction"]()).toEqualTypeOf<foundry.grid.BaseGrid.MeasurePathCostFunction | void>();
+expectTypeOf(ruler["_computeDistance"]()).toBeVoid();
+
+declare const fullRulerSegment: Ruler.MeasurementSegment;
+expectTypeOf(ruler["_getSegmentLabel"](fullRulerSegment)).toBeString();
+expectTypeOf(ruler["_getSegmentLabel"]({ distance: 50, last: false, teleport: false })).toBeString();
+
+expectTypeOf(ruler["_drawMeasuredPath"]()).toBeVoid();
+
+declare const someRay: Ray;
+expectTypeOf(ruler["_highlightMeasurementSegment"](fullRulerSegment)).toBeVoid();
+expectTypeOf(ruler["_highlightMeasurementSegment"]({ teleport: true, ray: someRay })).toBeVoid();
+
+expectTypeOf(ruler.moveToken()).toEqualTypeOf<Promise<boolean>>();
+expectTypeOf(ruler["_getMovementToken"](somePoint)).toEqualTypeOf<Token.ConfiguredInstance | null>();
+
+expectTypeOf(ruler["_getMeasurementHistory"]()).toEqualTypeOf<Ruler.MeasurementHistory | void>();
+expectTypeOf(ruler["_createMeasurementHistory"]()).toEqualTypeOf<Ruler.MeasurementHistory>();
+
+declare const someToken: Token.ConfiguredInstance;
+expectTypeOf(ruler["_canMove"](someToken)).toBeBoolean();
+expectTypeOf(ruler["_animateMovement"](someToken)).toEqualTypeOf<Promise<void>>();
+
+expectTypeOf(
+  ruler["_animateSegment"](someToken, fullRulerSegment, somePoint, { noHook: false, recursive: true }),
+).toEqualTypeOf<Promise<void>>();
+expectTypeOf(
+  ruler["_animateSegment"](
+    someToken,
+    {
+      animation: {
+        // TODO: fill in once properly typed in TokenDocument
+      },
+      teleport: true,
+    },
+    somePoint,
+    {
+      noHook: false,
+      recursive: true,
+    },
+  ),
+).toEqualTypeOf<Promise<void>>();
 
-expectTypeOf(ruler.measure(new PIXI.Point())).toEqualTypeOf<Array<Ruler.Segment>>();
-expectTypeOf(ruler.measure({ x: 10, y: 10 }, {})).toEqualTypeOf<Array<Ruler.Segment>>();
-expectTypeOf(ruler.measure({ x: 10, y: 10 }, { gridSpaces: true })).toEqualTypeOf<Array<Ruler.Segment>>();
+expectTypeOf(ruler["_preMove"](someToken)).toEqualTypeOf<Promise<void>>();
+expectTypeOf(ruler["_postMove"](someToken)).toEqualTypeOf<Promise<void>>();
+expectTypeOf(ruler["_broadcastMeasurement"]()).toBeVoid();
+expectTypeOf(ruler["_getMeasurementData"]()).toEqualTypeOf<Ruler.MeasurementData>();
 
-expectTypeOf(ruler.moveToken()).toEqualTypeOf<Promise<false | undefined>>();
-expectTypeOf(ruler.clear()).toEqualTypeOf<void>();
-expectTypeOf(ruler.toJSON()).toEqualTypeOf<RulerData>();
-expectTypeOf(ruler.update(ruler.toJSON())).toEqualTypeOf<void>();
+expectTypeOf(ruler.update()).toBeVoid();
+expectTypeOf(ruler.update(null)).toBeVoid();
+expectTypeOf(
+  ruler.update({
+    destination: somePoint,
+    history: [],
+    state: Ruler.STATES.MEASURING,
+    token: someToken.id,
+    waypoints: [],
+  }),
+).toBeVoid();