From 3cfbb695e901fe1d1a7d56edc7888b4f936ab47c Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:01:20 -0400 Subject: [PATCH] [Docs] Update `battler-tags.ts` module doc comment for clarity (#6658) --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battler-tags.ts | 95 ++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 38 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 032bc20d243..1d493908010 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,39 +1,51 @@ /** - * BattlerTags are used to represent semi-persistent effects that can be attached to a Pokemon. - * Note that before serialization, a new tag object is created, and then `loadTag` is called on the - * tag with the object that was serialized. + * `BattlerTag`s are used to represent semi-persistent effects attached to individual Pokemon. * - * This means it is straightforward to avoid serializing fields. - * Fields that are not set in the constructor and not set in `loadTag` will thus not be serialized. + * During serialization, a new blank tag object is created, before its `loadTag` is called + * with the object that was serialized. \ + * This makes it fairly straightforward to avoid serializing fields — anything not set in the class constructor + * or the tag's `loadTag` method will not be serialized. * - * Any battler tag that can persist across sessions must extend SerializableBattlerTag in its class definition signature. - * Only tags that persist across waves (meaning their effect can last >1 turn) should be considered - * serializable. + * Any battler tag that can persist across waves (meaning it lasts longer than 1 turn) + * **must extend `SerializableBattlerTag`** in its class definition signature. * - * Serializable battler tags have strict requirements for their fields. - * Properties that are not necessary to reconstruct the tag must not be serialized. This can be avoided - * by using a private property. If access to the property is needed outside of the class, then - * a getter (and potentially, a setter) should be used instead. + * `SerializableBattlerTag`s have strict requirements for their fields: + * - Properties that are not necessary to reconstruct the tag **must not be serialized** + * This can be accomplished by using {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_elements | private elements} + * and/or {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#getters--setters | `getters`}. \ + * Note that `getter`s do not need to be tied to a class field, for example: + * ```ts + * class Example { + * public get thing(): number { + * return 5; + * } + * } + * ``` * - * If a property that is intended to be private must be serialized, then it should instead - * be declared as a public readonly propety. Then, in the `loadTag` method (or any method inside the class that needs to adjust the property) - * use `(this as Mutable).propertyName = value;` - * These rules ensure that Typescript is aware of the shape of the serialized version of the class. + * - If a property that is intended to be "private" should be serialized, it **must** + * be declared as `public readonly` instead. + * Then, in the `loadTag` method (or any internal method that needs to adjust the property), + * use a cast to `Mutable` (such as `(this as Mutable).propertyName = value`). \ + * This ensures that Typescript is aware of the shape of the serialized version of the class. * - * If any new serializable fields *are* added, then the class *must* override the - * `loadTag` method to set the new fields. Its signature *must* match the example below: - * ``` - * class ExampleTag extends SerializableBattlerTag { - * // Example, if we add 2 new fields that should be serialized: - * public a: string; - * public b: number; - * // Then we must also define a loadTag method with one of the following signatures - * public override loadTag(source: BaseBattlerTag & Pick(source: BaseBattlerTag & Pick): void; - * } - * ``` - * Notes - * - If the class has any subclasses, then the second form of `loadTag` *must* be used. + * - If any new serializable fields _are_ added, then the class **must** override the + * `loadTag` method to set the new fields. \ + * Its signature must match the example below: + * ```ts + * class ExampleTag extends SerializableBattlerTag { + * // Example, if we add 2 new fields that should be serialized: + * public a: string; + * public b: number; + * // Then we must also define a loadTag method with one of the following signatures: + * // This signature should be used if the class has no subclasses: + * public override loadTag(source: BaseBattlerTag & Pick(source: BaseBattlerTag & Pick): void { + * this.a = source.a; + * this.b = source.b; + * } + * } + * ``` * @module */ @@ -92,13 +104,14 @@ import { coerceArray } from "#utils/array"; import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#utils/common"; import { toCamelCase } from "#utils/strings"; -/** Interface containing the serializable fields of BattlerTag */ +/** Interface containing the serializable fields of `BattlerTag` */ interface BaseBattlerTag { - /** The tag's remaining duration */ + /** The tag's remaining duration. */ + // TODO: Add support for omitting `turnCount` turnCount: number; - /** The {@linkcode MoveId} that created this tag, or `undefined` if not set by a move */ + /** The {@linkcode MoveId} that created this tag, or `undefined` if not set by a move. */ sourceMove?: MoveId; - /** The {@linkcode Pokemon.id | PID} of the Pokemon that added this tag, or `undefined` if not set by a pokemon */ + /** The {@linkcode Pokemon.id | PID} of the Pokemon that added this tag, or `undefined` if not set by a Pokemon. */ sourceId?: number; } @@ -144,6 +157,7 @@ export class BattlerTag implements BaseBattlerTag { this.#lapseTypes = coerceArray(lapseType); this.turnCount = turnCount; // We intentionally don't want to set source move to `MoveId.NONE` here, so a raw boolean comparison is OK. + // TODO: Rework tags passing `MoveId.NONE` to instead pass `undefined` for consistency if (sourceMove) { this.sourceMove = sourceMove; } @@ -195,7 +209,7 @@ export class BattlerTag implements BaseBattlerTag { /** * Load the data for a given {@linkcode BattlerTag} or JSON representation thereof. * Should be inherited from by any battler tag with custom attributes. - * @param source - An object containing the fields needed to reconstruct this tag. + * @param source - An object containing the fields needed to reconstruct this tag */ public loadTag(source: BaseBattlerTag & Pick): void { this.turnCount = source.turnCount; @@ -205,7 +219,7 @@ export class BattlerTag implements BaseBattlerTag { /** * Helper function that retrieves the source Pokemon object - * @returns The source {@linkcode Pokemon}, or `null` if none is found + * @returns The source {@linkcode Pokemon}, or `undefined` if none is found */ public getSourcePokemon(): Pokemon | undefined { return globalScene.getPokemonById(this.sourceId); @@ -213,7 +227,12 @@ export class BattlerTag implements BaseBattlerTag { } export class SerializableBattlerTag extends BattlerTag { - /** Nonexistent, dummy field to allow typescript to distinguish this class from `BattlerTag` */ + /** + * Nonexistent, dummy field to allow typescript to distinguish this class from `BattlerTag`. + * + * @remarks + * Does not exist at runtime, so must not be used! + */ private declare __SerializableBattlerTag: never; } @@ -223,7 +242,7 @@ export class SerializableBattlerTag extends BattlerTag { * * @remarks * Used to ensure type safety when serializing battler tags, - * allowing typescript to properly infer the type of the tag. + * allowing Typescript to properly infer the type of the tag. * @see BattlerTagTypeMap */ interface GenericSerializableBattlerTag extends SerializableBattlerTag {