record_fields_ordering
Flags a record type annotation whose named fields are not in the order you configure.
A record is structurally typed, so its named fields are a set rather than a sequence — ({int a, int b}) and ({int b, int a}) are the same type. That makes their written order pure presentation, and an inconsistent one means the same type reads differently in every place it is spelled out.
Positional fields are never ordered: their position is their identity, and reordering them makes a different type.
This rule is in the pedantic preset, which sets order: alphabetical. Under any other preset it reports nothing until you set order:.
many_lints: rules: record_fields_ordering: order: alphabeticalrules: record_fields_ordering: order: alphabeticalWith order: alphabetical:
typedef ApiResult = ({String message, int code, bool retryable});typedef ApiResult = ({int code, String message, bool retryable});Examples
Section titled “Examples”Any record annotation is checked, not just typedefs
Section titled “Any record annotation is checked, not just typedefs”A return type, a parameter type and a variable’s declared type all carry the same annotation and are all ordered:
rules: record_fields_ordering: order: alphabetical// Don't — a return type({String name, int age}) parseUser(String raw) => (name: raw, age: 0);
// Don't — a parameter typevoid render(({String label, int width}) box) {}
// Don't — a declared variable type({String city, int zip}) address = (city: 'Warsaw', zip: 0);// Do({int age, String name}) parseUser(String raw) => (age: 0, name: raw);
void render(({String label, int width}) box) {}
({String city, int zip}) address = (city: 'Warsaw', zip: 0);Positional fields are left alone
Section titled “Positional fields are left alone”Only the named half of a mixed record is ordered:
rules: record_fields_ordering: order: alphabetical// Don't — `int, String` positional pair untouched, but `zebra` precedes `apple`typedef Row = (int, String, {int zebra, int apple});
// Dotypedef Row = (int, String, {int apple, int zebra});by_length
Section titled “by_length”Longest field name last. Ties fall back to alphabetical so the order is total:
rules: record_fields_ordering: order: by_length// Don'ttypedef Size = ({int millimetres, int cm, int metres});
// Dotypedef Size = ({int cm, int metres, int millimetres});Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
order |
string | — |
alphabetical, alphabetical_case_sensitive or by_length. Unset means the rule is silent |
Known limitations
Section titled “Known limitations”Only the type annotation is checked, never the literal. (name: 'a', age: 1) is a record expression, and the rule does not visit it — a record literal’s named fields carry no annotation to read.
Fewer than two named fields are never reported — a single field cannot be out of order, and a record with only positional fields is skipped entirely.
Only the first out-of-order field is reported, so one misplaced name does not produce a wall of diagnostics.
No quick fix. Reordering named fields is type-safe, but the annotation is often mirrored by literals elsewhere that a reader will want to keep in step.
An unrecognised order: falls back to alphabetical rather than throwing, because a plugin cannot report a diagnostic against a YAML file.
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: record_fields_ordering: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”pattern_fields_ordering— Keep pattern fields in a configured order.enum_constants_ordering— Keep enum constants in a configured order.arguments_ordering— Keep named arguments in a configured order.initializers_ordering— Keep constructor initializers in field order.