Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #3184 (2025-02-19 01:45:03)

Types and tests for `client/pixi/layers/effects/**` (other than `CanvasVisibility`) and associated changes
couple changes to config/hooks

edit: also touch up `WeatherShaderEffect` and `AbstractWeatherShader` and subclasses


Patch:
diff --git a/src/foundry/client/config.d.mts b/src/foundry/client/config.d.mts
index 6df7dcab9..45e78ad2a 100644
--- a/src/foundry/client/config.d.mts
+++ b/src/foundry/client/config.d.mts
@@ -1653,7 +1653,7 @@ declare global {
      * Available Weather Effects implementations
      */
     weatherEffects: {
-      [key: string]: CONFIG.WeatherAmbienceConfiguration;
+      [key: string]: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1668,7 +1668,7 @@ declare global {
        * }
        * ```
        */
-      leaves: CONFIG.WeatherAmbienceConfiguration;
+      leaves: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1696,7 +1696,7 @@ declare global {
        * }
        * ```
        */
-      rain: CONFIG.WeatherAmbienceConfiguration;
+      rain: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1737,7 +1737,7 @@ declare global {
        * }
        * ```
        */
-      rainStorm: CONFIG.WeatherAmbienceConfiguration;
+      rainStorm: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1762,7 +1762,7 @@ declare global {
        * }
        * ```
        */
-      fog: CONFIG.WeatherAmbienceConfiguration;
+      fog: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1788,7 +1788,7 @@ declare global {
        * }
        * ```
        * */
-      snow: CONFIG.WeatherAmbienceConfiguration;
+      snow: WeatherEffects.AmbienceConfiguration;
 
       /**
        * @defaultValue
@@ -1826,7 +1826,7 @@ declare global {
        * }
        * ```
        */
-      blizzard: CONFIG.WeatherAmbienceConfiguration;
+      blizzard: WeatherEffects.AmbienceConfiguration;
     };
 
     /**
@@ -3132,23 +3132,6 @@ declare global {
       }
     }
 
-    interface WeatherAmbienceConfiguration {
-      id: string;
-      label: string;
-      filter: {
-        enabled: boolean;
-        blendMode: PIXI.BLEND_MODES;
-      };
-      effects: WeatherEffectConfiguration;
-    }
-
-    interface WeatherEffectConfiguration {
-      id: string;
-      effectClass: ParticleEffect | WeatherShaderEffect.AnyConstructor;
-      blendMode: PIXI.BLEND_MODES;
-      config: Record<string, unknown>;
-    }
-
     // The point of this interface is to be declaration merged into so you can override `DefaultSpecialStatusEffects` and remove existing keys. It's never used when empty.
     // eslint-disable-next-line @typescript-eslint/no-empty-object-type
     interface SpecialStatusEffects {}
diff --git a/src/foundry/client/hooks.d.mts b/src/foundry/client/hooks.d.mts
index 082cea7c4..561286c4b 100644
--- a/src/foundry/client/hooks.d.mts
+++ b/src/foundry/client/hooks.d.mts
@@ -487,7 +487,7 @@ declare global {
        */
       initializeWeatherEffects: (
         weatherEffect: WeatherEffects,
-        weatherEffectsConfig?: WeatherEffects.WeatherEffectsConfig | null,
+        weatherEffectsConfig?: WeatherEffects.EffectConfiguration | null,
       ) => void;
 
       /** Adventure */
diff --git a/src/foundry/client/pixi/core/containers/cached-container.d.mts b/src/foundry/client/pixi/core/containers/cached-container.d.mts
index dd4ff015f..75a28f3c3 100644
--- a/src/foundry/client/pixi/core/containers/cached-container.d.mts
+++ b/src/foundry/client/pixi/core/containers/cached-container.d.mts
@@ -113,7 +113,7 @@ declare global {
       multisample: PIXI.MSAA_QUALITY;
       scaleMode: PIXI.SCALE_MODES;
       format: PIXI.FORMATS;
-      /** @privateRemarks Only exists on DarknessLevelContainer and is seemingly unused there */
+      /** @remarks Only exists on DarknessLevelContainer and is seemingly unused there */
       mipmap: PIXI.MIPMAP_MODES;
     }>;
 
diff --git a/src/foundry/client/pixi/layers/effects/background-effects.d.mts b/src/foundry/client/pixi/layers/effects/background-effects.d.mts
index d6df7c5aa..a9b95b815 100644
--- a/src/foundry/client/pixi/layers/effects/background-effects.d.mts
+++ b/src/foundry/client/pixi/layers/effects/background-effects.d.mts
@@ -1,4 +1,4 @@
-import type { HandleEmptyObject } from "../../../../../utils/index.d.mts";
+import type { HandleEmptyObject } from "fvtt-types/utils";
 
 declare global {
   /**
@@ -7,13 +7,11 @@ declare global {
   class CanvasBackgroundAlterationEffects extends CanvasLayer {
     /**
      * A collection of effects which provide background vision alterations.
-     * @defaultValue `vision.sortableChildren = true`
      */
     vision: PIXI.Container;
 
     /**
      * A collection of effects which provide background preferred vision alterations.
-     * @defaultValue `visionPreferred.sortableChildren = true`
      */
     visionPreferred: PIXI.Container;
 
@@ -35,6 +33,7 @@ declare global {
   }
 
   namespace CanvasBackgroundAlterationEffects {
+    interface Any extends AnyCanvasBackgroundAlterationEffects {}
     type AnyConstructor = typeof AnyCanvasBackgroundAlterationEffects;
 
     interface DrawOptions extends CanvasLayer.DrawOptions {}
diff --git a/src/foundry/client/pixi/layers/effects/coloration-effects.d.mts b/src/foundry/client/pixi/layers/effects/coloration-effects.d.mts
index 44dcac1e1..c740e1466 100644
--- a/src/foundry/client/pixi/layers/effects/coloration-effects.d.mts
+++ b/src/foundry/client/pixi/layers/effects/coloration-effects.d.mts
@@ -1,4 +1,4 @@
-import type { HandleEmptyObject } from "../../../../../utils/index.d.mts";
+import type { HandleEmptyObject } from "fvtt-types/utils";
 
 declare global {
   /**
@@ -12,8 +12,9 @@ declare global {
 
     /**
      * The filter used to mask visual effects on this layer
+     * @remarks Only `undefined` prior to first draw
      */
-    filter: VisualEffectsMaskingFilter;
+    filter: VisualEffectsMaskingFilter.ConfiguredInstance | undefined;
 
     /**
      * Clear coloration effects container
@@ -26,6 +27,7 @@ declare global {
   }
 
   namespace CanvasColorationEffects {
+    interface Any extends AnyCanvasColorationEffects {}
     type AnyConstructor = typeof AnyCanvasColorationEffects;
 
     interface DrawOptions extends CanvasLayer.DrawOptions {}
diff --git a/src/foundry/client/pixi/layers/effects/darkness-effects.d.mts b/src/foundry/client/pixi/layers/effects/darkness-effects.d.mts
index 6a6265cf4..1d70d2e52 100644
--- a/src/foundry/client/pixi/layers/effects/darkness-effects.d.mts
+++ b/src/foundry/client/pixi/layers/effects/darkness-effects.d.mts
@@ -1,4 +1,4 @@
-import type { HandleEmptyObject } from "../../../../../utils/index.d.mts";
+import type { HandleEmptyObject } from "fvtt-types/utils";
 
 declare global {
   /**
@@ -15,13 +15,16 @@ declare global {
      */
     clear(): void;
 
-    override _draw(options: HandleEmptyObject<CanvasDarknessEffects.DrawOptions>): Promise<void>;
+    protected override _draw(options: HandleEmptyObject<CanvasDarknessEffects.DrawOptions>): Promise<void>;
   }
 
   namespace CanvasDarknessEffects {
+    interface Any extends AnyCanvasDarknessEffects {}
     type AnyConstructor = typeof AnyCanvasDarknessEffects;
 
     interface DrawOptions extends CanvasLayer.DrawOptions {}
+
+    interface TearDownOptions extends CanvasLayer.TearDownOptions {}
   }
 }
 
diff --git a/src/foundry/client/pixi/layers/effects/illumination-effects.d.mts b/src/foundry/client/pixi/layers/effects/illumination-effects.d.mts
index 8a96390d2..c0ce3f686 100644
--- a/src/foundry/client/pixi/layers/effects/illumination-effects.d.mts
+++ b/src/foundry/client/pixi/layers/effects/illumination-effects.d.mts
@@ -1,4 +1,4 @@
-import type { HandleEmptyObject, NullishProps } from "fvtt-types/utils";
+import type { HandleEmptyObject } from "fvtt-types/utils";
 
 declare global {
   /**
@@ -7,8 +7,9 @@ declare global {
   class CanvasIlluminationEffects extends CanvasLayer {
     /**
      * The filter used to mask visual effects on this layer
+     * @remarks Only `undefined` prior to first draw
      */
-    filter: VisualEffectsMaskingFilter | undefined;
+    filter: VisualEffectsMaskingFilter.ConfiguredInstance | undefined;
 
     /**
      * The container holding the lights.
@@ -42,8 +43,12 @@ declare global {
 
     /**
      * Set or retrieve the illumination background color.
+     * @remarks Foundry types this as `number` but it gets passed to {@link Color.from}
      */
-    set backgroundColor(color: number);
+    set backgroundColor(color: Color.Source);
+
+    /** @remarks This getter doesn't actually exist, it's only here to correct the type inferred from the setter */
+    get backgroundColor(): undefined;
 
     /**
      * Clear illumination effects container
@@ -55,7 +60,7 @@ declare global {
      * @param force - Force cached container invalidation?
      *                (default: `false`)
      */
-    invalidateDarknessLevelContainer(force: boolean): void;
+    invalidateDarknessLevelContainer(force?: boolean | null): void;
 
     /**
      * Create the background color texture used by illumination point source meshes.
@@ -88,17 +93,19 @@ declare global {
 
     /**
      * @deprecated since v12, will be removed in v14
+     * @remarks "CanvasIlluminationEffects#background is now obsolete."
      */
-    background: null;
+    background(): null;
 
     /**
      * @deprecated since v12, will be removed in v14
-     * @remarks `"CanvasIlluminationEffects#globalLight has been deprecated without replacement. Check the canvas.environment.globalLightSource.active instead."`
+     * @remarks "CanvasIlluminationEffects#globalLight has been deprecated without replacement. Check the canvas.environment.globalLightSource.active instead."
      */
     get globalLight(): boolean;
   }
 
   namespace CanvasIlluminationEffects {
+    interface Any extends AnyCanvasIlluminationEffects {}
     type AnyConstructor = typeof AnyCanvasIlluminationEffects;
 
     interface DrawOptions extends CanvasLayer.DrawOptions {}
@@ -123,19 +130,15 @@ declare global {
      * }
      * ```
      */
-    static override textureConfiguration: NullishProps<{
-      multisample: PIXI.MSAA_QUALITY;
-      scaleMode: PIXI.SCALE_MODES;
-      format: PIXI.FORMATS;
-      mipmap: PIXI.MIPMAP_MODES;
-    }>;
+    static override textureConfiguration: CachedContainer.TextureConfiguration;
 
     /** @privateRemarks Including to protect duck typing due to overall similarities b/w DarknessLevelContainer and CachedContainer */
     #onChildChange(): void;
   }
 
   namespace DarknessLevelContainer {
-    type AnyConstructor = typeof AnyCanvasIlluminationEffects;
+    interface Any extends AnyDarknessLevelContainer {}
+    type AnyConstructor = typeof AnyDarknessLevelContainer;
   }
 }
 
diff --git a/src/foundry/client/pixi/layers/effects/weather-effects.d.mts b/src/foundry/client/pixi/layers/effects/weather-effects.d.mts
index 0fd341af7..672de4350 100644
--- a/src/foundry/client/pixi/layers/effects/weather-effects.d.mts
+++ b/src/foundry/client/pixi/layers/effects/weather-effects.d.mts
@@ -1,4 +1,4 @@
-import type { HandleEmptyObject, InexactPartial } from "fvtt-types/utils";
+import type { HandleEmptyObject, InexactPartial, IntentionalPartial, NullishProps } from "fvtt-types/utils";
 
 declare global {
   /**
@@ -6,7 +6,7 @@ declare global {
    */
   class WeatherEffects extends FullCanvasObjectMixin(CanvasLayer) {
     /**
-     * @privateRemarks This is not overridden in foundry but reflects the real behavior.
+     * @privateRemarks This is not overridden in foundry but reflects the real behavior, due to `layerOptions.name` being `"effects"`
      */
     static get instance(): Canvas["effects"];
 
@@ -22,13 +22,13 @@ declare global {
 
     /**
      * The container in which effects are added.
-     * @defaultValue `undefined`
+     * @remarks Only `undefined` prior to first draw
      */
     weatherEffects: PIXI.Container | undefined;
 
     /**
      * The container in which suppression meshed are added.
-     * @defaultValue `undefined`
+     * @remarks Only `undefined` prior to first draw
      */
     suppression: PIXI.Container | undefined;
 
@@ -37,6 +37,7 @@ declare global {
      */
     static override get layerOptions(): WeatherEffects.LayerOptions;
 
+    /** @privateRemarks Types-only override to sync with static `layerOptions` */
     override options: WeatherEffects.LayerOptions;
 
     /**
@@ -48,19 +49,18 @@ declare global {
      * A default configuration of the terrain mask that is automatically applied to any shader-based weather effects.
      * This configuration is automatically passed to WeatherShaderEffect#configureTerrainMask upon construction.
      *
-     * @privateRemarks This property is checked and conditionally passed in two places in this file,
-     * but never set as far as I can tell. It is not initialized to a value.
+     * @privateRemarks In practice this is always `undefined`, as it is not initialized to a value and never gets set anywhere in core in v12.331
      */
-    terrainMaskConfig: WeatherEffects.WeatherTerrainMaskConfiguration | undefined;
+    terrainMaskConfig: WeatherEffects.MaskConfiguration | undefined;
 
     /**
      * A default configuration of the terrain mask that is automatically applied to any shader-based weather effects.
      * This configuration is automatically passed to WeatherShaderEffect#configureTerrainMask upon construction.
      *
-     * @privateRemarks This property is passed to `WeatherEffects.configureOcclusionMask` in two places, but both times
-     * as `this.occlusionMaskConfig || {enabled: true}`, and it is never set as far as I can tell.
+     * @privateRemarks In practice this is always `undefined`, as it is not initalized to a value and never gets set anywhere in core in v12.331.
+     * Passed as `this.occlusionMaskConfig || { enabled: true }` where it is used.
      */
-    occlusionMaskConfig: WeatherEffects.WeatherOcclusionMaskConfiguration | undefined;
+    occlusionMaskConfig: WeatherEffects.MaskConfiguration | undefined;
 
     /**
      * The inverse occlusion mask filter bound to this container.
@@ -69,16 +69,17 @@ declare global {
 
     /**
      * The elevation of this object.
-     * @throws The setter throws an error if passed NaN
+     * @defaultValue `Infinity`
+     * @throws If `NaN` is passed to the setter
      */
     get elevation(): number;
 
-    set elevation(value: number);
+    set elevation(value);
 
     /**
      * A key which resolves ties amongst objects at the same elevation of different layers.
      * @defaultValue `PrimaryCanvasGroup.SORT_LAYERS.WEATHER`
-     * @throws The setter throws an error if passed NaN
+     * @throws If `NaN` is passed to the setter
      */
     get sortLayer(): number;
 
@@ -87,19 +88,19 @@ declare global {
     /**
      * A key which resolves ties amongst objects at the same elevation within the same layer.
      * @defaultValue `0`
-     * @throws The setter throws an error if passed NaN
+     * @throws If `NaN` is passed to the setter
      */
     get sort(): number;
 
-    set sort(value: number);
+    set sort(value);
 
     /**
      * A key which resolves ties amongst objects at the same elevation within the same layer and same sort.
-     * @throws The setter throws an error if passed NaN
+     * @throws If `NaN` is passed to the setter
      */
     get zIndex(): number;
 
-    set zIndex(value: number);
+    set zIndex(value);
 
     protected override _draw(options: HandleEmptyObject<WeatherEffects.DrawOptions>): Promise<void>;
 
@@ -108,13 +109,12 @@ declare global {
     /**
      * Initialize the weather container from a weather config object.
      * @param weatherEffectsConfig - Weather config object (or null/undefined to clear the container).
-     *
-     * @privateRemarks Foundry has this typed as just `object`, but explicitly calls it with null and implicitly with undefined
      */
-    initializeEffects(weatherEffectsConfig?: WeatherEffects.WeatherEffectsConfig | null): void;
+    initializeEffects(weatherEffectsConfig?: WeatherEffects.AmbienceConfiguration | null): void;
 
     /**
      * Clear the weather container.
+     * @remarks Just calls `initializeEffects(null)`
      */
     clearEffects(): void;
 
@@ -123,10 +123,9 @@ declare global {
      * @param context - The shader context
      * @param config  - Occlusion masking options
      */
-    protected static configureOcclusionMask(
+    static configureOcclusionMask(
       context: PIXI.Shader,
-      /** @remarks Can't be NullishProps because `texture` is only checked for `!== undefined` */
-      config?: InexactPartial<WeatherEffects.WeatherOcclusionMaskConfiguration>,
+      config?: WeatherEffects.MaskConfiguration, // not:null (destructured)
     ): void;
 
     /**
@@ -134,10 +133,9 @@ declare global {
      * @param context - The shader context
      * @param config  - Terrain masking options
      */
-    protected static configureTerrainMask(
+    static configureTerrainMask(
       context: PIXI.Shader,
-      /** @remarks Can't be NullishProps because `texture` is only checked for `!== undefined` */
-      config?: InexactPartial<WeatherEffects.WeatherTerrainMaskConfiguration>,
+      config?: WeatherEffects.MaskConfiguration, // not:null (destructured)
     ): void;
 
     /**
@@ -148,9 +146,11 @@ declare global {
   }
 
   namespace WeatherEffects {
+    interface Any extends AnyWeatherEffects {}
     type AnyConstructor = typeof AnyWeatherEffects;
 
     interface LayerOptions extends CanvasLayer.LayerOptions {
+      /** @remarks This causes `WeatherEffect.instance` to be the `EffectsCanvasGroup` */
       name: "effects";
     }
 
@@ -158,63 +158,138 @@ declare global {
 
     interface TearDownOptions extends CanvasLayer.TearDownOptions {}
 
-    interface WeatherTerrainMaskConfiguration {
+    /**
+     * @internal
+     * @privateRemarks If `WeatherEffects#occlusionMaskConfig` or `#terrainMaskConfig` were *ever* set, i.e
+     * if this interface was ever used for more than a parameter type, it would have a base interface with
+     * no optionality/nullishness applied. Luckily it's not.
+     */
+    type _MaskConfiguration = NullishProps<{
       /**
        * Enable or disable this mask.
        * @defaultValue `false`
        */
       enabled: boolean;
 
-      /**
-       * An RGBA array of channel weights applied to the mask texture.
-       * @defaultValue `[1, 0, 0, 0]`
-       */
-      channelWeights: [r: number, b: number, g: number, a: number];
-
       /**
        * If the mask should be reversed.
        * @defaultValue `false`
        */
-      reverse?: boolean | undefined;
-
+      reverse: boolean;
+    }> &
+      InexactPartial<{
+        /**
+         * An RGBA array of channel weights applied to the mask texture.
+         * @defaultValue `[0, 0, 1, 0]`
+         * @remarks Can't be `null` as it only has a parameter default.
+         */
+        channelWeights: [r: number, b: number, g: number, a: number];
+
+        /**
+         * A texture which defines the mask region.
+         * @remarks Can't be `null` because of an `!== undefined` check.
+         *
+         * If not provided, the shader being modified will have its `uniforms.useTerrain` or `.useOcclusion` set `false`
+         */
+        texture: PIXI.Texture | PIXI.RenderTexture;
+      }>;
+
+    interface MaskConfiguration extends _MaskConfiguration {}
+
+    type AmbienceFilterConfiguration = IntentionalPartial<{
       /**
-       * A texture which defines the mask region.
+       * Enable a layer-wide occlusion filter unless it is explicitly disabled by the effect configuration
+       * @remarks The above should for clarity say `ambience configuration` instead of `effect configuration`;
+       * Individual effects of this ambience can enable occlusion on themselves if this is set false, but
+       * cannot disable layer-wide occlusion if this is `true`
+       *
+       * @privateRemarks Checked for `!== false`, so allowing nullish values that get handled as `true` would be confusing
        */
-      texture: PIXI.Texture | PIXI.RenderTexture;
-    }
-
-    interface WeatherOcclusionMaskConfiguration {
+      enabled: boolean;
+    }> &
+      NullishProps<{
+        /**
+         * @defaultValue `PIXI.BLEND_MODES.NORMAL`
+         * @remarks The blend mode applied to the `WeatherEffects#occlusionFilter`
+         */
+        blendMode: PIXI.BLEND_MODES;
+      }>;
+
+    /** @internal */
+    type _CommonEffectConfiguration = NullishProps<{
       /**
-       * Enable or disable this mask.
-       * @defaultValue `false`
+       * @defaultValue `CONST.CANVAS_PERFORMANCE_MODES.LOW`
+       * @remarks The minimum required performance level to render this effect
        */
-      enabled: boolean;
+      performanceLevel: CONST.CANVAS_PERFORMANCE_MODES;
 
       /**
-       * An RGBA array of channel weights applied to the mask texture.
-       * @defaultValue `[0, 0, 1, 0]`
+       * @defaultValue The index of this effect in its array
+       * @remarks Applied to the constructed `effectClass`. None of the effects in core ambience definitions provide one.
        */
-      channelWeights: [r: number, b: number, g: number, a: number];
+      zIndex: number;
 
       /**
-       * If the mask should be reversed.
-       * @defaultValue `false`
+       * @defaultValue `PIXI.BLEND_MODES.NORMAL`
+       * @remarks Applied to the constructed `effectClass`
        */
-      reverse: boolean;
+      blendMode: PIXI.BLEND_MODES;
+    }>;
+
+    interface ParticleEffectConfiguration extends _CommonEffectConfiguration {
+      id: string;
+
+      effectClass: ParticleEffect.AnyConstructor;
 
       /**
-       * A texture which defines the mask region.
+       * @remarks Despite the corresponding `ParticleEffect#constructor` parameter being `={}`, construction will
+       * throw if this isn't a non-empty object, **except** in the case of the only core `extends ParticleEffect`
+       * class, {@link AutumnLeavesWeatherEffect | `AutumnLeavesWeatherEffect`}, which overrides the relevant method and always uses its static
+       * `LEAF_CONFIG` property instead; accounting for this is the only reason the property is optional
        */
-      texture: PIXI.Texture | PIXI.RenderTexture;
+      config: PIXI.particles.EmitterConfigV3;
     }
 
-    /**
-     * @privateRemarks types inferred from usage
-     */
-    interface WeatherEffectsConfig {
-      effects: WeatherEffects[];
+    interface SpecificallyAutumnLeavesConfiguration {
+      id: string;
+
+      effectClass: AutumnLeavesWeatherEffect.AnyConstructor;
+
+      /** @remarks {@link AutumnLeavesWeatherEffect | `AutumnLeavesWeatherEffect`} overrides {@link ParticleEffect.getParticleEmitters | `ParticleEffect#getParticleEmitters`} -- the method that would throw when passed an empty config -- to not take any parameters and always use */
+      config?: never;
+    }
+
+    interface WeatherShaderEffectConfiguration extends _CommonEffectConfiguration {
+      id: string;
+
+      effectClass: WeatherShaderEffect.AnyConstructor;
+
+      shaderClass: AbstractWeatherShader.AnyConstructor;
+
+      /** @remarks Can't be `null` because it gets `Object.entries()`ed with only a parameter default */
+      config?: WeatherShaderEffect.Configuration | undefined;
+    }
+
+    type EffectConfiguration =
+      | SpecificallyAutumnLeavesConfiguration
+      | ParticleEffectConfiguration
+      | WeatherShaderEffectConfiguration;
+
+    /** @internal */
+    type _AmbienceConfiguration = NullishProps<{
+      /** @remarks Properties of `filter` are always accessed with optional chaining in v12.331  */
+      filter: AmbienceFilterConfiguration;
+    }>;
+
+    interface AmbienceConfiguration extends _AmbienceConfiguration {
+      /** @remarks Should match the key for this ambience in the parent object (`CONFIG.Canvas.weatherEffects`) */
+      id: string;
+
+      /** @remarks A localization key to display in the Configure Scene sheet */
+      label: string;
 
-      filter: PIXI.Filter;
+      /** @remarks One or more effect definitions */
+      effects: EffectConfiguration[];
     }
   }
 }
diff --git a/src/foundry/client/pixi/layers/effects/weather/particles/effect.d.mts b/src/foundry/client/pixi/layers/effects/weather/particles/effect.d.mts
index 7cf1bbda7..28c8ba983 100644
--- a/src/foundry/client/pixi/layers/effects/weather/particles/effect.d.mts
+++ b/src/foundry/client/pixi/layers/effects/weather/particles/effect.d.mts
@@ -8,11 +8,10 @@ declare global {
     /**
      * @param options - Options passed to the getParticleEmitters method which can be used to customize values of the emitter configuration.
      *                  (default: `{}`)
+     * @remarks Despite being an `={}` parameter, construction will throw if it is passed (or defaults to via omission) an empty object
      */
     constructor(options: PIXI.particles.EmitterConfigV3);
 
-    parent: PIXI.Container;
-
     emitters: PIXI.particles.Emitter[];
 
     /**
@@ -28,7 +27,8 @@ declare global {
      * Subclasses can override this method for more advanced configurations.
      * @param options - Options provided to the ParticleEffect constructor which can be used to customize
      *                  configuration values for created emitters. (default: `{}`)
-     * @throws An error if `foundry.utils.isEmpty(options)`
+     * @throws If `options` is passed as (or defaults to via omission) an empty object
+     * @remarks Despite its name, this method only takes a single config object and only returns a single-element array.
      */
     getParticleEmitters(options: PIXI.particles.EmitterConfigV3): PIXI.particles.Emitter[];
 
@@ -44,4 +44,13 @@ declare global {
      */
     stop(): void;
   }
+
+  namespace ParticleEffect {
+    interface Any extends AnyParticleEffect {}
+    type AnyConstructor = typeof AnyParticleEffect;
+  }
+}
+
+declare abstract class AnyParticleEffect extends ParticleEffect {
+  constructor(arg0: never, ...args: never[]);
 }
diff --git a/src/foundry/client/pixi/layers/effects/weather/particles/leaves.d.mts b/src/foundry/client/pixi/layers/effects/weather/particles/leaves.d.mts
index 1d427d6c1..513c46061 100644
--- a/src/foundry/client/pixi/layers/effects/weather/particles/leaves.d.mts
+++ b/src/foundry/client/pixi/layers/effects/weather/particles/leaves.d.mts
@@ -5,16 +5,45 @@ declare global {
    * A full-screen weather effect which renders gently falling autumn leaves.
    */
   class AutumnLeavesWeatherEffect extends ParticleEffect {
+    /**
+     * @remarks `options` is completely unused in this subclass, as it gets passed to `#getParticleEmitters`, which, unlike super's method, takes no arguments
+     */
+    constructor(options?: PIXI.particles.EmitterConfigV3 | null);
     /**
      * @defaultValue `"WEATHER.AutumnLeaves"`
+     * @remarks A localization key. Appears to be unused in v12.331, probably vestigial, is identical to the
+     * `label` for the AmbienceConfig containing this effect in `CONFIG.Canvas.weatherEffects`
      */
     static label: string;
 
     /**
      * Configuration for the particle emitter for falling leaves
+     * @remarks Not a complete EmitterConfigV3; the `frequency` and `pos` required keys are omitted. `frequency` is set
+     * up in `#getParticleEmitters`, but `pos` is not; it is erroneously marked as required in the interface,
+     * see https://github.com/pixijs-userland/particle-emitter/issues/219
      */
-    static LEAF_CONFIG: PIXI.particles.EmitterConfigV3;
+    static LEAF_CONFIG: AutumnLeavesWeatherEffect.LeafConfig;
 
+    /** @remarks This does not take a config paramater, unlike its parent class; uses `static LEAF_CONFIG` on this class instead */
     override getParticleEmitters(): PIXI.particles.Emitter[];
   }
+
+  namespace AutumnLeavesWeatherEffect {
+    interface Any extends AnyAutumnLeavesWeatherEffect {}
+    type AnyConstructor = typeof AnyAutumnLeavesWeatherEffect;
+
+    /** @internal */
+    interface _EmitterConfigOptionalProperties {
+      pos?: PIXI.particles.EmitterConfigV3["pos"];
+      frequency?: PIXI.particles.EmitterConfigV3["frequency"];
+    }
+
+    interface LeafConfig
+      extends Omit<PIXI.particles.EmitterConfigV3, keyof _EmitterConfigOptionalProperties>,
+        _EmitterConfigOptionalProperties {}
+  }
+}
+
+declare abstract class AnyAutumnLeavesWeatherEffect extends AutumnLeavesWeatherEffect {
+  constructor(arg0: never, ...args: never[]);
 }
diff --git a/src/foundry/client/pixi/webgl/shaders/base-shader.d.mts b/src/foundry/client/pixi/webgl/shaders/base-shader.d.mts
index 3a25816a0..640ca775c 100644
--- a/src/foundry/client/pixi/webgl/shaders/base-shader.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/base-shader.d.mts
@@ -12,7 +12,7 @@ declare global {
     constructor(
       program: PIXI.Program,
       /** @defaultValue `{}` */
-      uniforms?: AbstractBaseShader.Uniforms,
+      uniforms?: AbstractBaseShader.Uniforms | null,
     );
 
     /**
@@ -47,7 +47,7 @@ declare global {
      */
     static create<ThisType extends AbstractBaseShader.AnyConstructor>(
       this: ThisType,
-      initialUniforms?: AbstractBaseShader.Uniforms,
+      initialUniforms?: AbstractBaseShader.Uniforms | null,
     ): FixedInstanceType<ThisType>;
 
     /**
diff --git a/src/foundry/client/pixi/webgl/shaders/filters/base-filter.d.mts b/src/foundry/client/pixi/webgl/shaders/filters/base-filter.d.mts
index 6b00dc41f..8271dc7a9 100644
--- a/src/foundry/client/pixi/webgl/shaders/filters/base-filter.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/filters/base-filter.d.mts
@@ -33,7 +33,7 @@ declare global {
       /**
        * @defaultValue `{}`
        */
-      initialUniforms?: AbstractBaseShader.Uniforms,
+      initialUniforms?: AbstractBaseShader.Uniforms | null,
     ): FixedInstanceType<ThisType>;
   }
 
diff --git a/src/foundry/client/pixi/webgl/shaders/filters/glow-overlay.d.mts b/src/foundry/client/pixi/webgl/shaders/filters/glow-overlay.d.mts
index 033a47eb5..07253a303 100644
--- a/src/foundry/client/pixi/webgl/shaders/filters/glow-overlay.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/filters/glow-overlay.d.mts
@@ -54,7 +54,7 @@ declare global {
 
     static override create<ThisType extends AbstractBaseFilter.AnyConstructor>(
       this: ThisType,
-      initialUniforms?: AbstractBaseShader.Uniforms,
+      initialUniforms?: AbstractBaseShader.Uniforms | null,
     ): FixedInstanceType<ThisType>;
 
     override apply(
diff --git a/src/foundry/client/pixi/webgl/shaders/filters/outline-overlay.d.mts b/src/foundry/client/pixi/webgl/shaders/filters/outline-overlay.d.mts
index 6a9f37625..7f3724ac7 100644
--- a/src/foundry/client/pixi/webgl/shaders/filters/outline-overlay.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/filters/outline-overlay.d.mts
@@ -53,7 +53,7 @@ declare global {
 
     static override create<ThisType extends AbstractBaseFilter.AnyConstructor>(
       this: ThisType,
-      initialUniforms?: AbstractBaseShader.Uniforms,
+      initialUniforms?: AbstractBaseShader.Uniforms | null,
     ): FixedInstanceType<ThisType>;
 
     override apply(
diff --git a/src/foundry/client/pixi/webgl/shaders/filters/visibility.d.mts b/src/foundry/client/pixi/webgl/shaders/filters/visibility.d.mts
index cf3d51c7a..6fc5106bd 100644
--- a/src/foundry/client/pixi/webgl/shaders/filters/visibility.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/filters/visibility.d.mts
@@ -27,8 +27,8 @@ declare global {
 
     static override create<ThisType extends AbstractBaseFilter.AnyConstructor>(
       this: ThisType,
-      initiaUniforms?: AbstractBaseShader.Uniforms,
-      options?: VisibilityFilter.FragmentShaderOptions,
+      initiaUniforms?: AbstractBaseShader.Uniforms | null,
+      options?: VisibilityFilter.FragmentShaderOptions, // not:null (passed to `this.fragmentShader()` where its properties are accesed unsafely),
     ): FixedInstanceType<ThisType>;
 
     static override vertexShader: string;
diff --git a/src/foundry/client/pixi/webgl/shaders/weather/base-weather.d.mts b/src/foundry/client/pixi/webgl/shaders/weather/base-weather.d.mts
index a38e6e744..6d5db6455 100644
--- a/src/foundry/client/pixi/webgl/shaders/weather/base-weather.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/weather/base-weather.d.mts
@@ -1,9 +1,4 @@
-import type {
-  InterfaceToObject,
-  AnyObject,
-  RemoveIndexSignatures,
-  FixedInstanceType,
-} from "../../../../../../utils/index.d.mts";
+import type { RemoveIndexSignatures, FixedInstanceType } from "../../../../../../utils/index.d.mts";
 
 type AbstractBaseShaderClass = typeof AbstractBaseShader;
 
@@ -17,16 +12,18 @@ declare const InternalAbstractWeatherShader_Const: InternalAbstractWeatherShader
 
 // @ts-expect-error - This pattern inherently requires a ts-expect-error as the base class is dynamic.
 class InternalAbstractWeatherShader<
-  DefaultUniforms extends AnyObject,
-  _ComputedUniforms extends object = RemoveIndexSignatures<Extract<DefaultUniforms, AnyObject>>,
+  DefaultUniforms extends AbstractBaseShader.Uniforms,
+  _ComputedUniforms extends object = RemoveIndexSignatures<DefaultUniforms>,
 > extends InternalAbstractWeatherShader_Const<_ComputedUniforms> {}
 
 declare global {
   /**
    * The base shader class for weather shaders.
+   * @typeParam DefaultUniforms - An interface representing an `AbstractWeatherShader` subclass's `static defaultUniforms`
+   * @remarks For each key in `static defaultOptions`, dynamically defines a getter/setter pair for `this.uniforms[key]` on the instance
    */
   class AbstractWeatherShader<
-    DefaultUniforms extends AnyObject = InterfaceToObject<AbstractWeatherShader.DefaultUniforms>,
+    DefaultUniforms extends AbstractBaseShader.Uniforms = AbstractWeatherShader.DefaultUniforms,
   > extends InternalAbstractWeatherShader<DefaultUniforms> {
     constructor(...args: ConstructorParameters<typeof AbstractBaseShader>);
 
@@ -49,7 +46,7 @@ declare global {
 
     static override create<ThisType extends AbstractBaseShader.AnyConstructor>(
       this: ThisType,
-      initialUniforms?: AbstractBaseShader.Uniforms,
+      initialUniforms?: AbstractBaseShader.Uniforms | null,
     ): FixedInstanceType<ThisType>;
 
     /**
@@ -65,6 +62,9 @@ declare global {
      */
     set scale(scale: number | { x: number; y?: number });
 
+    /** @remarks No getter is actually provided */
+    get scale(): undefined;
+
     set scaleX(x: number);
 
     set scaleY(y: number);
@@ -101,11 +101,10 @@ declare global {
       time: number;
     }
 
-    // eslint-disable-next-line @typescript-eslint/no-empty-object-type
-    interface DefaultUniforms {}
+    interface DefaultUniforms extends AbstractBaseShader.Uniforms {}
   }
 }
 
-declare abstract class AnyAbstractWeatherShader extends AbstractWeatherShader {
+declare abstract class AnyAbstractWeatherShader extends AbstractWeatherShader<AbstractWeatherShader.DefaultUniforms> {
   constructor(arg0: never, ...args: never[]);
 }
diff --git a/src/foundry/client/pixi/webgl/shaders/weather/effect.d.mts b/src/foundry/client/pixi/webgl/shaders/weather/effect.d.mts
index bece86fe0..b9806e255 100644
--- a/src/foundry/client/pixi/webgl/shaders/weather/effect.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/weather/effect.d.mts
@@ -1,37 +1,30 @@
-import type { InexactPartial, FixedInstanceType } from "fvtt-types/utils";
-
-type QuadMeshClass = typeof QuadMesh;
-
-interface InternalWeatherShaderEffect_Interface extends QuadMeshClass {
-  new <ShaderClassInstance extends object>(
-    ...args: ConstructorParameters<typeof QuadMesh>
-  ): QuadMesh & ShaderClassInstance;
-}
-
-declare const InternalWeatherShaderEffect_Const: InternalWeatherShaderEffect_Interface;
-
-// @ts-expect-error - This pattern inherently requires a ts-expect-error as the base class is dynamic.
-class InternalWeatherShaderEffect<
-  ShaderClass extends AbstractWeatherShader.AnyConstructor,
-  _ShaderClassInstance extends object = FixedInstanceType<ShaderClass>,
-> extends InternalWeatherShaderEffect_Const<_ShaderClassInstance> {}
+import type { IntentionalPartial } from "fvtt-types/utils";
 
 declare global {
   /**
    * An interface for defining shader-based weather effects
    */
-  class WeatherShaderEffect<
-    ShaderClass extends AbstractWeatherShader.AnyConstructor = AbstractWeatherShader.AnyConstructor,
-  > extends InternalWeatherShaderEffect<ShaderClass> {
+  class WeatherShaderEffect extends QuadMesh {
     /**
      * @param config - The config object to create the shader effect
      */
-    constructor(config: WeatherShaderEffect.WeatherShaderEffectConfig | undefined, shaderClass: ShaderClass);
+    constructor(
+      config: WeatherShaderEffect.Configuration | undefined,
+      shaderClass: AbstractWeatherShader.AnyConstructor,
+    );
+
+    /** @privateRemarks Override not in Foundry code, but reflects reality at runtime */
+    override get shader(): AbstractWeatherShader;
+
+    /** @privateRemarks Override not in Foundry code, but reflects reality at runtime */
+    override setShaderClass(shaderClass: AbstractWeatherShader.AnyConstructor): void;
 
     /**
      * Set shader parameters.
      */
-    configure(config?: WeatherShaderEffect.WeatherShaderEffectConfig): void;
+    configure(
+      config?: WeatherShaderEffect.Configuration, // not:null (gets `Object.entries()`ed)
+    ): void;
 
     /**
      * Begin animation
@@ -47,17 +40,17 @@ declare global {
      * Initialize the weather effect.
      * @param config - Config object.
      */
-    protected _initialize(config: WeatherShaderEffect.WeatherShaderEffectConfig): void;
+    protected _initialize(config: WeatherShaderEffect.Configuration): void;
   }
 
   namespace WeatherShaderEffect {
     interface Any extends AnyWeatherShaderEffect {}
     type AnyConstructor = typeof AnyWeatherShaderEffect;
 
-    type WeatherShaderEffectConfig = InexactPartial<AbstractBaseShader.Uniforms & WeatherShaderEffect["shader"]>;
+    type Configuration = IntentionalPartial<AbstractBaseShader.Uniforms & AbstractWeatherShader>;
   }
 }
 
-declare abstract class AnyWeatherShaderEffect extends WeatherShaderEffect<AbstractWeatherShader.AnyConstructor> {
+declare abstract class AnyWeatherShaderEffect extends WeatherShaderEffect {
   constructor(arg0: never, ...args: never[]);
 }
diff --git a/src/foundry/client/pixi/webgl/shaders/weather/fog.d.mts b/src/foundry/client/pixi/webgl/shaders/weather/fog.d.mts
index 590fa6766..4ffb65c44 100644
--- a/src/foundry/client/pixi/webgl/shaders/weather/fog.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/weather/fog.d.mts
@@ -1,4 +1,4 @@
-import type { InterfaceToObject } from "fvtt-types/utils";
+export {};
 
 declare global {
   /**
@@ -6,7 +6,7 @@ declare global {
    */
   class FogShader<
     DefaultUniforms extends FogShader.DefaultUniforms = FogShader.DefaultUniforms,
-  > extends AbstractWeatherShader<InterfaceToObject<DefaultUniforms>> {
+  > extends AbstractWeatherShader<DefaultUniforms> {
     /**
      * @defaultValue
      * ```js
@@ -38,7 +38,7 @@ declare global {
     interface Any extends AnyFogShader {}
     type AnyConstructor = typeof AnyFogShader;
 
-    interface DefaultUniforms extends AbstractBaseShader.Uniforms, AbstractWeatherShader.DefaultUniforms {
+    interface DefaultUniforms extends AbstractWeatherShader.DefaultUniforms {
       intensity: number;
       rotation: number;
       slope: number;
diff --git a/src/foundry/client/pixi/webgl/shaders/weather/rain.d.mts b/src/foundry/client/pixi/webgl/shaders/weather/rain.d.mts
index 6ba3a315e..6aad9e433 100644
--- a/src/foundry/client/pixi/webgl/shaders/weather/rain.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/weather/rain.d.mts
@@ -1,4 +1,4 @@
-import type { InterfaceToObject } from "fvtt-types/utils";
+export {};
 
 declare global {
   /**
@@ -6,7 +6,7 @@ declare global {
    */
   class RainShader<
     DefaultUniforms extends RainShader.DefaultUniforms = RainShader.DefaultUniforms,
-  > extends AbstractWeatherShader<InterfaceToObject<DefaultUniforms>> {
+  > extends AbstractWeatherShader<DefaultUniforms> {
     /**
      * @defaultValue
      * ```js
@@ -28,7 +28,7 @@ declare global {
     interface Any extends AnyRainShader {}
     type AnyConstructor = typeof AnyRainShader;
 
-    interface DefaultUniforms extends AbstractBaseShader.Uniforms {
+    interface DefaultUniforms extends AbstractWeatherShader.DefaultUniforms {
       opacity: number;
       intensity: number;
       strength: number;
diff --git a/src/foundry/client/pixi/webgl/shaders/weather/snow.d.mts b/src/foundry/client/pixi/webgl/shaders/weather/snow.d.mts
index 34cf4e6ab..f146629c6 100644
--- a/src/foundry/client/pixi/webgl/shaders/weather/snow.d.mts
+++ b/src/foundry/client/pixi/webgl/shaders/weather/snow.d.mts
@@ -1,4 +1,4 @@
-import type { InterfaceToObject } from "fvtt-types/utils";
+export {};
 
 declare global {
   /**
@@ -6,7 +6,7 @@ declare global {
    */
   class SnowShader<
     DefaultUniforms extends SnowShader.DefaultUniforms = SnowShader.DefaultUniforms,
-  > extends AbstractWeatherShader<InterfaceToObject<DefaultUniforms>> {
+  > extends AbstractWeatherShader<DefaultUniforms> {
     /**
      * @defaultValue
      * ```
@@ -24,7 +24,7 @@ declare global {
     interface Any extends AnySnowShader {}
     type AnyConstructor = typeof AnySnowShader;
 
-    interface DefaultUniforms extends AbstractBaseShader.Uniforms {
+    interface DefaultUniforms extends AbstractWeatherShader.DefaultUniforms {
       direction: number;
     }
   }


Test patch:
diff --git a/tests/foundry/client/pixi/layers/effects/background-effects.test-d.ts b/tests/foundry/client/pixi/layers/effects/background-effects.test-d.ts
new file mode 100644
index 000000000..adb3c37f8
--- /dev/null
+++ b/tests/foundry/client/pixi/layers/effects/background-effects.test-d.ts
@@ -0,0 +1,11 @@
+import { expectTypeOf } from "vitest";
+
+const layer = new CanvasBackgroundAlterationEffects();
+
+expectTypeOf(layer.options.baseClass).toEqualTypeOf<typeof CanvasLayer>();
+expectTypeOf(layer.vision).toEqualTypeOf<PIXI.Container>();
+expectTypeOf(layer.visionPreferred).toEqualTypeOf<PIXI.Container>();
+expectTypeOf(layer.lighting).toEqualTypeOf<PIXI.Container>();
+expectTypeOf(layer.draw()).toEqualTypeOf<Promise<CanvasBackgroundAlterationEffects>>();
+expectTypeOf(layer["_draw"]({})).toEqualTypeOf<Promise<void>>();
+expectTypeOf(layer.clear()).toBeVoid();
diff --git a/tests/foundry/client/pixi/layers/effects/coloration-effects.test-d.ts b/tests/foundry/client/pixi/layers/effects/coloration-effects.test-d.ts
index 6803468b9..a179f4cb8 100644
--- a/tests/foundry/client/pixi/layers/effects/coloration-effects.test-d.ts
+++ b/tests/foundry/client/pixi/layers/effects/coloration-effects.test-d.ts
@@ -3,4 +3,7 @@ import { expectTypeOf } from "vitest";
 const layer = new CanvasColorationEffects();
 
 expectTypeOf(layer.options.baseClass).toEqualTypeOf<typeof CanvasLayer>();
+expectTypeOf(layer.filter).toEqualTypeOf<VisualEffectsMaskingFilter.ConfiguredInstance | undefined>();
 expectTypeOf(layer.draw()).toEqualTypeOf<Promise<CanvasColorationEffects>>();
+expectTypeOf(layer["_draw"]({})).toEqualTypeOf<Promise<void>>();
+expectTypeOf(layer.clear()).toBeVoid();
diff --git a/tests/foundry/client/pixi/layers/effects/darkness-effects.test-d.ts b/tests/foundry/client/pixi/layers/effects/darkness-effects.test-d.ts
new file mode 100644
index 000000000..bb2aa8630
--- /dev/null
+++ b/tests/foundry/client/pixi/layers/effects/darkness-effects.test-d.ts
@@ -0,0 +1,8 @@
+import { expectTypeOf } from "vitest";
+
+const layer = new CanvasDarknessEffects();
+
+expectTypeOf(layer.options.baseClass).toEqualTypeOf<typeof CanvasLayer>();
+expectTypeOf(layer.draw()).toEqualTypeOf<Promise<CanvasDarknessEffects>>();
+expectTypeOf(layer["_draw"]({})).toEqualTypeOf<Promise<void>>();
+expectTypeOf(layer.clear()).toBeVoid();
diff --git a/tests/foundry/client/pixi/layers/effects/illumination-effects.test-d.ts b/tests/foundry/client/pixi/layers/effects/illumination-effects.test-d.ts
index e6a8fbb87..1d4c6cf6e 100644
--- a/tests/foundry/client/pixi/layers/effects/illumination-effects.test-d.ts
+++ b/tests/foundry/client/pixi/layers/effects/illumination-effects.test-d.ts
@@ -3,4 +3,34 @@ import { expectTypeOf } from "vitest";
 const layer = new CanvasIlluminationEffects();
 
 expectTypeOf(layer.options.baseClass).toEqualTypeOf<typeof CanvasLayer>();
+
+expectTypeOf(layer.filter).toEqualTypeOf<VisualEffectsMaskingFilter.ConfiguredInstance | undefined>();
+expectTypeOf(layer.lights).toEqualTypeOf<PIXI.Container>();
+expectTypeOf(layer.backgroundColorTexture).toEqualTypeOf<PIXI.Texture>();
+expectTypeOf(layer.baselineMesh).toEqualTypeOf<SpriteMesh>();
+expectTypeOf(layer.darknessLevelMeshes).toEqualTypeOf<DarknessLevelContainer>();
+
+expectTypeOf(layer.hasDynamicDarknessLevel).toBeBoolean();
+expectTypeOf(layer.renderTexture).toEqualTypeOf<PIXI.RenderTexture>();
+
+// getter not actually defined
+expectTypeOf(layer.backgroundColor).toBeUndefined();
+layer.backgroundColor = 0xfeafdd;
+layer.backgroundColor = "#123456";
+layer.backgroundColor = [0.2, 0.4, 0.9];
+
+expectTypeOf(layer.clear()).toBeVoid();
+expectTypeOf(layer.invalidateDarknessLevelContainer(true)).toBeVoid();
+expectTypeOf(layer["_createBackgroundColorTexture"]()).toEqualTypeOf<PIXI.Texture>();
+
 expectTypeOf(layer.draw()).toEqualTypeOf<Promise<CanvasIlluminationEffects>>();
+expectTypeOf(layer["_draw"]({})).toEqualTypeOf<Promise<void>>();
+
+//deprecated until v13
+expectTypeOf(layer.updateGlobalLight()).toEqualTypeOf<false>();
+//deprecated until v14
+expectTypeOf(layer.background()).toBeNull();
+expectTypeOf(layer.globalLight).toBeBoolean();
+
+// Literally no instance changes to test on DLC
+expectTypeOf(DarknessLevelContainer.textureConfiguration).toEqualTypeOf<CachedContainer.TextureConfiguration>();
diff --git a/tests/foundry/client/pixi/layers/effects/weather-effects.test-d.ts b/tests/foundry/client/pixi/layers/effects/weather-effects.test-d.ts
index c678e3e21..c85bd94ac 100644
--- a/tests/foundry/client/pixi/layers/effects/weather-effects.test-d.ts
+++ b/tests/foundry/client/pixi/layers/effects/weather-effects.test-d.ts
@@ -1,10 +1,120 @@
 import { expectTypeOf } from "vitest";
 
+// not an error, Foundry really does this for some reason
 expectTypeOf(WeatherEffects.instance).toEqualTypeOf<EffectsCanvasGroup | undefined>();
 expectTypeOf(WeatherEffects.layerOptions).toEqualTypeOf<WeatherEffects.LayerOptions>();
 expectTypeOf(WeatherEffects.layerOptions.name).toEqualTypeOf<"effects">();
 
+declare class SomeWeatherShader extends AbstractWeatherShader {}
+declare const someShader: SomeWeatherShader;
+declare const someTex: PIXI.Texture;
+expectTypeOf(WeatherEffects.configureOcclusionMask(someShader)).toBeVoid();
+expectTypeOf(WeatherEffects.configureOcclusionMask(someShader, {})).toBeVoid();
+expectTypeOf(
+  WeatherEffects.configureOcclusionMask(someShader, {
+    channelWeights: [1, 0, 0, 0],
+    enabled: true,
+    reverse: false,
+    texture: someTex,
+  }),
+).toBeVoid();
+
+expectTypeOf(WeatherEffects.configureTerrainMask(someShader)).toBeVoid();
+expectTypeOf(WeatherEffects.configureTerrainMask(someShader, {})).toBeVoid();
+expectTypeOf(
+  WeatherEffects.configureTerrainMask(someShader, {
+    channelWeights: undefined,
+    enabled: null,
+    reverse: true,
+    texture: someTex,
+  }),
+).toBeVoid();
+
 const layer = new WeatherEffects();
+
+expectTypeOf(layer.weatherEffects).toEqualTypeOf<PIXI.Container | undefined>();
+expectTypeOf(layer.suppression).toEqualTypeOf<PIXI.Container | undefined>();
+
 expectTypeOf(layer.options.name).toEqualTypeOf<"effects">();
-expectTypeOf(layer.tearDown()).toEqualTypeOf<Promise<WeatherEffects | void>>();
+
+expectTypeOf(layer.effects).toEqualTypeOf<Map<string, (ParticleEffect | WeatherShaderEffect)[]>>();
+expectTypeOf(layer.terrainMaskConfig).toEqualTypeOf<WeatherEffects.MaskConfiguration | undefined>();
+expectTypeOf(layer.occlusionMaskConfig).toEqualTypeOf<WeatherEffects.MaskConfiguration | undefined>();
+
+expectTypeOf(layer.elevation).toBeNumber();
+expectTypeOf(layer.sortLayer).toBeNumber();
+expectTypeOf(layer.sort).toBeNumber();
+expectTypeOf(layer.zIndex).toBeNumber();
+
 expectTypeOf(layer.draw()).toEqualTypeOf<Promise<WeatherEffects>>();
+expectTypeOf(layer["_draw"]({})).toEqualTypeOf<Promise<void>>();
+expectTypeOf(layer.tearDown()).toEqualTypeOf<Promise<WeatherEffects>>();
+expectTypeOf(layer["_tearDown"]({})).toEqualTypeOf<Promise<void>>();
+
+declare class SomeParticleEffect extends ParticleEffect {}
+declare class SomeLeavesEffect extends AutumnLeavesWeatherEffect {}
+declare class SomeWeatherShaderEffect extends WeatherShaderEffect {}
+
+expectTypeOf(layer.initializeEffects()).toBeVoid();
+expectTypeOf(layer.initializeEffects(null)).toBeVoid();
+expectTypeOf(layer.initializeEffects(undefined)).toBeVoid();
+expectTypeOf(
+  layer.initializeEffects({
+    id: "someID",
+    label: "MYSYSTEM.WeatherEffects.someID.label",
+    filter: {
+      enabled: false,
+      blendMode: PIXI.BLEND_MODES.DIFFERENCE,
+    },
+    effects: [
+      //@ts-expect-error `AutumnLeavesWeatherEffect` does not take a config object as it overrides that path and uses its `static LEAF_CONFIG` instead
+      {
+        id: "leaves2",
+        effectClass: SomeLeavesEffect,
+        config: {},
+      },
+      {
+        id: "someParticleEffect",
+        effectClass: SomeParticleEffect,
+        blendMode: PIXI.BLEND_MODES.LUMINOSITY,
+        performanceLevel: CONST.CANVAS_PERFORMANCE_MODES.HIGH,
+        config: {
+          // a PIXI.particles.EmitterConfigV3
+          lifetime: { max: 5, min: 2 },
+          frequency: 0.25,
+          pos: { x: 0, y: 10 },
+          behaviors: [
+            {
+              type: "someBehaviourType",
+              config: {
+                foo: 7,
+                bar: "string",
+              },
+            },
+          ],
+        },
+      },
+      {
+        id: "someWeatherEffect",
+        effectClass: SomeWeatherShaderEffect,
+        shaderClass: SomeWeatherShader,
+        performanceLevel: CONST.CANVAS_PERFORMANCE_MODES.MED,
+        blendMode: null,
+        config: {
+          // UniformValues
+          foo: 7.6,
+          bar: [1, 2, 3, 4],
+          baz: new PIXI.Matrix(),
+          // properties of AbstractWeatherShader
+          scaleX: 2.2,
+          scaleY: 3.5,
+        },
+      },
+    ],
+  }),
+).toBeVoid();
+
+expectTypeOf(layer.clearEffects()).toBeVoid();
+
+//deprecated until v13
+expectTypeOf(layer.weather).toEqualTypeOf<typeof layer>();
diff --git a/tests/foundry/client/pixi/layers/effects/weather/leaves.test-d.ts b/tests/foundry/client/pixi/layers/effects/weather/leaves.test-d.ts
index 183984f19..7fa5ea748 100644
--- a/tests/foundry/client/pixi/layers/effects/weather/leaves.test-d.ts
+++ b/tests/foundry/client/pixi/layers/effects/weather/leaves.test-d.ts
@@ -2,4 +2,6 @@ import { expectTypeOf } from "vitest";
 
 const myLeaves = new AutumnLeavesWeatherEffect();
 
+//@ts-expect-error LEAF_CONFIG is not a fully valid `EmitterConfigV3`, it is completed inside `#getParticleEmitters`
 expectTypeOf(myLeaves.createEmitter(AutumnLeavesWeatherEffect.LEAF_CONFIG)).toEqualTypeOf<PIXI.particles.Emitter>();
+expectTypeOf(myLeaves.getParticleEmitters()).toEqualTypeOf<PIXI.particles.Emitter[]>();
diff --git a/tests/foundry/client/pixi/layers/effects/weather/particles/effect.test-d.ts b/tests/foundry/client/pixi/layers/effects/weather/particles/effect.test-d.ts
new file mode 100644
index 000000000..b3b4e2c15
--- /dev/null
+++ b/tests/foundry/client/pixi/layers/effects/weather/particles/effect.test-d.ts
@@ -0,0 +1,23 @@
+import { expectTypeOf } from "vitest";
+
+const emitterConfig = {
+  lifetime: { max: 5, min: 5 },
+  frequency: 3,
+  pos: { x: 0, y: 0 },
+  behaviors: [
+    {
+      type: "someBehaviourType",
+      config: {
+        foo: 7,
+        bar: "string",
+      },
+    },
+  ],
+};
+const myEffect = new ParticleEffect(emitterConfig);
+
+expectTypeOf(myEffect.createEmitter(emitterConfig)).toEqualTypeOf<PIXI.particles.Emitter>();
+expectTypeOf(myEffect.getParticleEmitters(emitterConfig)).toEqualTypeOf<PIXI.particles.Emitter[]>();
+
+expectTypeOf(myEffect.play()).toBeVoid();
+expectTypeOf(myEffect.stop()).toBeVoid();
diff --git a/tests/foundry/client/pixi/layers/effects/weather/particles/leaves.test-d.ts b/tests/foundry/client/pixi/layers/effects/weather/particles/leaves.test-d.ts
new file mode 100644
index 000000000..7fa5ea748
--- /dev/null
+++ b/tests/foundry/client/pixi/layers/effects/weather/particles/leaves.test-d.ts
@@ -0,0 +1,7 @@
+import { expectTypeOf } from "vitest";
+
+const myLeaves = new AutumnLeavesWeatherEffect();
+
+//@ts-expect-error LEAF_CONFIG is not a fully valid `EmitterConfigV3`, it is completed inside `#getParticleEmitters`
+expectTypeOf(myLeaves.createEmitter(AutumnLeavesWeatherEffect.LEAF_CONFIG)).toEqualTypeOf<PIXI.particles.Emitter>();
+expectTypeOf(myLeaves.getParticleEmitters()).toEqualTypeOf<PIXI.particles.Emitter[]>();
diff --git a/tests/foundry/client/pixi/webgl/shaders/weather/base-weather.test-d.ts b/tests/foundry/client/pixi/webgl/shaders/weather/base-weather.test-d.ts
index 5ebc023fa..ab092dcc9 100644
--- a/tests/foundry/client/pixi/webgl/shaders/weather/base-weather.test-d.ts
+++ b/tests/foundry/client/pixi/webgl/shaders/weather/base-weather.test-d.ts
@@ -1,6 +1,10 @@
 import { expectTypeOf } from "vitest";
 
-const AWS = AbstractWeatherShader;
+declare class MyWeatherShader extends AbstractWeatherShader<{
+  foo: number;
+  bar: [number, number];
+}> {}
+const AWS = MyWeatherShader;
 let myAWS;
 
 expectTypeOf(AWS.fragmentShader).toEqualTypeOf<string | AbstractBaseShader.FragmentShaderFunction>();
@@ -10,3 +14,7 @@ expectTypeOf((myAWS = AWS.create())).toEqualTypeOf<AbstractWeatherShader>();
 
 expectTypeOf(myAWS.speed).toEqualTypeOf<number>();
 expectTypeOf(myAWS["_preRender"]).toEqualTypeOf<AbstractBaseShader.PreRenderFunction>();
+
+//dynamic properties
+expectTypeOf(myAWS.foo).toBeNumber();
+expectTypeOf(myAWS.bar).toEqualTypeOf<[number, number]>();
diff --git a/tests/foundry/client/pixi/webgl/shaders/weather/fog.test-d.ts b/tests/foundry/client/pixi/webgl/shaders/weather/fog.test-d.ts
index 5505c0fd1..c6c80f246 100644
--- a/tests/foundry/client/pixi/webgl/shaders/weather/fog.test-d.ts
+++ b/tests/foundry/client/pixi/webgl/shaders/weather/fog.test-d.ts
@@ -11,3 +11,8 @@ expectTypeOf((myFS = FS.create())).toEqualTypeOf<FogShader>();
 
 expectTypeOf(myFS.speed).toEqualTypeOf<number>();
 expectTypeOf(myFS["_preRender"]).toEqualTypeOf<AbstractBaseShader.PreRenderFunction>();
+
+//dynamic properties from `FogShader.DefaultUniforms`
+expectTypeOf(myFS.intensity).toBeNumber();
+expectTypeOf(myFS.rotation).toBeNumber();
+expectTypeOf(myFS.slope).toBeNumber();
diff --git a/tests/foundry/client/pixi/webgl/shaders/weather/rain.test-d.ts b/tests/foundry/client/pixi/webgl/shaders/weather/rain.test-d.ts
index 4a82766e1..01a33faca 100644
--- a/tests/foundry/client/pixi/webgl/shaders/weather/rain.test-d.ts
+++ b/tests/foundry/client/pixi/webgl/shaders/weather/rain.test-d.ts
@@ -9,3 +9,10 @@ expectTypeOf((myRS = RS.create())).toEqualTypeOf<RainShader>();
 
 expectTypeOf(myRS.speed).toEqualTypeOf<number>();
 expectTypeOf(myRS["_preRender"]).toEqualTypeOf<AbstractBaseShader.PreRenderFunction>();
+
+//dynamic properties from `RainShader.DefaultUniforms`
+expectTypeOf(myRS.opacity).toBeNumber();
+expectTypeOf(myRS.intensity).toBeNumber();
+expectTypeOf(myRS.strength).toBeNumber();
+expectTypeOf(myRS.rotation).toBeNumber();
+expectTypeOf(myRS.resolution).toEqualTypeOf<[number, number]>();
diff --git a/tests/foundry/client/pixi/webgl/shaders/weather/snow.test-d.ts b/tests/foundry/client/pixi/webgl/shaders/weather/snow.test-d.ts
index 51ecf1937..c1ded41a5 100644
--- a/tests/foundry/client/pixi/webgl/shaders/weather/snow.test-d.ts
+++ b/tests/foundry/client/pixi/webgl/shaders/weather/snow.test-d.ts
@@ -9,3 +9,6 @@ expectTypeOf((mySS = SS.create())).toEqualTypeOf<SnowShader>();
 
 expectTypeOf(mySS.speed).toEqualTypeOf<number>();
 expectTypeOf(mySS["_preRender"]).toEqualTypeOf<AbstractBaseShader.PreRenderFunction>();
+
+//dynamic properties from `SnowShader.DefaultOptions`
+expectTypeOf(mySS.direction).toBeNumber();