Quick answer: Annotate the function with /// @returns {Struct.MyType} and define the struct via a constructor with /// @constructor MyType. Feather then validates struct properties correctly.

A helper function returns a struct with fields {x, y, name}. Calling it then reading result.name produces a Feather warning: “Variable name not set”. The code runs fine; only the IDE flags it. Hundreds of these warnings drown out real issues.

Why Feather Can’t Infer

GML is dynamically typed. Feather infers types from context: literal values, function signatures, JSDoc annotations. For a struct returned from a function, Feather has no way to know the field set unless you tell it.

The fix is two annotations: one on the struct definition, one on functions that produce or consume it.

Step 1: Define the Struct via Constructor

/// @function Vector2(x, y)
/// @param {Real} x
/// @param {Real} y
/// @returns {Struct.Vector2}
function Vector2(x, y) constructor {
    self.x = x;
    self.y = y;
}

Feather now recognizes Struct.Vector2 as a type with fields x and y.

Step 2: Annotate the Returning Function

/// @function compute_position(entity)
/// @param {Id.Instance} entity
/// @returns {Struct.Vector2}
function compute_position(entity) {
    return new Vector2(entity.x, entity.y);
}

Now compute_position(...).x validates cleanly. Feather knows the return is a Vector2 with x and y.

Step 3: For Ad-Hoc Structs, Use @returns Inline

If the struct doesn’t have a constructor (just a one-off):

/// @returns {Struct}
function make_config() {
    return {
        name: "default",
        speed: 100,
    };
}

Feather treats Struct as “any struct” and stops flagging field access — though it also won’t catch field-name typos. Use a named constructor when you want stricter checking.

Suppression for True False Positives

// Feather disable once GM2017
result.dynamic_field = 42;   // known dynamic, intentionally

The directive silences just the next line. Use sparingly — over-suppressing means real bugs go unnoticed.

Project-Wide Settings

File → Preferences → Language Features (Feather). Tune individual warning severities. For projects that intentionally use dynamic structs heavily, lower GM2017 (Unset variable) from Error to Hint to reduce noise while keeping it visible.

Verifying

Re-run Feather (Save triggers it). The previously-flagged lines should now be clean. If they’re still flagged, your constructor or @returns annotation isn’t reachable from the call site — check that both files are in the project and saved.

“Feather can’t infer what you don’t tell it. Annotate constructors and return types — warnings drop and real bugs surface.”

Adopt a project rule: every constructor gets a JSDoc block. Pays itself back the first time a typo is caught instead of running.