Mago 1.0.0-beta.19
This release introduces a major step forward in the type system's expressiveness and the analyzer's developer experience. The highlights are full support for object{...}
shapes for documenting plain objects, and beautiful type diffs in error messages to make debugging complex type mismatches easier than ever.
A huge thank you to @Bleksak for their significant contributions to this release!
✨ New: object{...}
Shape Support
Mago's PHPDoc parser and analyzer now have full, robust support for the object{...}
shape syntax, allowing you to precisely document the structure of generic objects with required and optional properties. This was a highly requested feature and the initial implementation was contributed by @Bleksak in #468
This includes support for:
- Sealed vs. Unsealed Shapes: Use
...
to specify if an object can have additional properties beyond those listed. - Optional Properties: Use
?
to mark properties that may not exist. - Precise Error Reporting: The analyzer provides new, more specific errors like
PossiblyNonExistentProperty
for optional properties and stricterNonExistentProperty
checks for sealed objects.
Example:
/**
* @param object{name: string, id?: string, ...} $user
*/
function process(object $user): void
{
echo $user->name; // no issues!
echo $user->id; // @mago-expect analysis:possibly-non-existent-property
echo $user->email; // @mago-expect analysis:ambiguous-object-property-access,mixed-argument
}
✨ New: Type Diffs in Error Messages
To dramatically improve the experience of debugging complex type errors, the analyzer now includes a colored, pretty-printed diff in the error report when a mismatch occurs between two complex types (like large array or object shapes). This makes it immediately obvious where the two types diverge, saving you time and effort. (#471)

🚀 Analyzer & Linter Improvements
- Analyzer: Subtype checking for unsealed
array{...}
shapes is now stricter and more correct, preventing unsafe assignments where an optional key in the destination could conflict with the source's generic value type. - Analyzer: Fixed a control flow bug where using
throw
on the right-hand side of a null coalesce operator (??
) would incorrectly mark subsequent code as unreachable. (#448) - Linter: The
use-compound-assignment
rule is now more accurate and will only apply to simple assignment operators. (Thanks @Bleksak!) (#472) - Linter: The
no-shorthand-ternary
rule has been relaxed to allow nested shorthand ternaries, a common pattern.
Closed Issues
Full Changelog: 1.0.0-beta.18...1.0.0-beta.19