From e60f2c66c5e5e813026c17f3d5b3b48824edc4bf Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 15 Sep 2025 20:23:38 -0400 Subject: [PATCH] Added restricted typing on matchers Now we can't call `expect(game).toHaveFullHp()`!!!!! --- test/@types/vitest.d.ts | 45 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/test/@types/vitest.d.ts b/test/@types/vitest.d.ts index fbf020b50a4..31688559484 100644 --- a/test/@types/vitest.d.ts +++ b/test/@types/vitest.d.ts @@ -10,6 +10,7 @@ import type { ArenaTagType } from "#enums/arena-tag-type"; import type { BattlerTagType } from "#enums/battler-tag-type"; import type { MoveId } from "#enums/move-id"; import type { PokemonType } from "#enums/pokemon-type"; +import type { PositionalTag } from "#data/positional-tags/positional-tag"; import type { PositionalTagType } from "#enums/positional-tag-type"; import type { BattleStat, EffectiveStat } from "#enums/stat"; import type { WeatherType } from "#enums/weather-type"; @@ -27,12 +28,26 @@ import type { AtLeastOne } from "#types/type-helpers"; import type { toDmgValue } from "#utils/common"; import type { expect } from "vitest"; -// #region Boilerplate +// #region Boilerplate/Helpers declare module "vitest" { - interface Assertion extends GenericMatchers, GameManagerMatchers, ArenaMatchers, PokemonMatchers {} + interface Assertion + extends GenericMatchers, + RestrictMatcher, + RestrictMatcher, + RestrictMatcher {} } -// #endregion Boilerplate +/** + * Utility type to restrict matchers' properties based on the type of `T`. + * If it does not extend `R`, all methods inside `M` will have their types resolved to `never`. + * @typeParam M - The type of the matchers object to restrict + * @typeParam T - The type parameter of the assertion + * @typeParam R - The type to restrict T based off of + */ +type RestrictMatcher = { + [k in keyof M]: T extends R ? M[k] : never; +}; +// #endregion Boilerplate/Helpers // #region Generic Matchers interface GenericMatchers { @@ -45,7 +60,7 @@ interface GenericMatchers { * @param expected - The expected contents of the array, in any order * @see {@linkcode expect.arrayContaining} */ - toEqualArrayUnsorted(expected: T extends (infer U)[] ? U[] : never): void; + toEqualArrayUnsorted: T extends (infer U)[] ? (expected: U[]) => void : never; /** * Check whether a {@linkcode Map} contains the given key, disregarding its value. @@ -56,12 +71,12 @@ interface GenericMatchers { * `expect(x).toContain[y, expect.anything()]`, * this is still preferred due to being more ergonomic and provides better error messsages. */ - toHaveKey(expectedKey: T extends Map ? K : never): void; + toHaveKey: T extends Map ? (expectedKey: K) => void : never; } // #endregion Generic Matchers // #region GameManager Matchers -interface GameManagerMatchers<_T> { +interface GameManagerMatchers { /** * Check if the {@linkcode GameManager} has shown the given message at least once in the current test case. * @param expectedMessage - The expected message to be displayed @@ -76,27 +91,25 @@ interface GameManagerMatchers<_T> { * @param expectedPhase - The expected {@linkcode PhaseString | name of the phase} */ toBeAtPhase(expectedPhase: PhaseString): void; -} - -// #endregion GameManager Matchers +} // #endregion GameManager Matchers // #region Arena Matchers -interface ArenaMatchers<_T> { +interface ArenaMatchers { /** * Check whether the current {@linkcode WeatherType} is as expected. - * @param expectedWeatherType - The expected {@linkcode WeatherType} + * @param expectedWeatherType - The expected `WeatherType` */ toHaveWeather(expectedWeatherType: WeatherType): void; /** * Check whether the current {@linkcode TerrainType} is as expected. - * @param expectedTerrainType - The expected {@linkcode TerrainType} + * @param expectedTerrainType - The expected `TerrainType` */ toHaveTerrain(expectedTerrainType: TerrainType): void; /** * Check whether the current {@linkcode Arena} contains the given {@linkcode ArenaTag}. - * @param expectedTag - A partially-filled {@linkcode ArenaTag} containing the desired properties + * @param expectedTag - A partially-filled `ArenaTag` containing the desired properties */ toHaveArenaTag(expectedTag: toHaveArenaTagOptions): void; /** @@ -114,15 +127,16 @@ interface ArenaMatchers<_T> { /** * Check whether the current {@linkcode Arena} contains the given number of {@linkcode PositionalTag}s. * @param expectedType - The {@linkcode PositionalTagType} of the desired tag - * @param count - The number of instances of {@linkcode expectedType} that should be active; + * @param count - The number of instances of `expectedType` that should be active; * defaults to `1` and must be within the range `[0, 4]` */ toHavePositionalTag(expectedType: PositionalTagType, count?: number): void; } + // #endregion Arena Matchers // #region Pokemon Matchers -interface PokemonMatchers<_T> { +interface PokemonMatchers { /** * Check whether a {@linkcode Pokemon}'s current typing includes the given types. * @param expectedTypes - The expected {@linkcode PokemonType}s to check against; must have length `>0` @@ -218,5 +232,4 @@ interface PokemonMatchers<_T> { */ toHaveUsedPP(moveId: MoveId, ppUsed: number | "all"): void; } - // #endregion Pokemon Matchers