Skip to content

Syntax Sugar and Shorthand Rules

8.1 Prefixes

Prefix formEquivalent formDescriptionExamples
?type["$.or", "void", type]Optional type; allows undefined"?string"
"?uint32"
!type["$.not", type]Negation; disallows the type"!string"
"!null"
@TypeNameReference a custom typeReference a registered or inline-defined type"@IPv4"
"@Email"
@TypeName(args)Reference with argumentsPass arguments to a custom type"@range(1,100)"
"@enum(a,b,c)"

Examples

typescript
// ?type: optional type
compiler.compile({ rule: { name: 'string', age: '?uint8' } });
// ✓ { name: "alice", age: 25 }
// ✓ { name: "bob" }  // age can be omitted

// !type: negation
compiler.compile({ rule: ['!string', '!null'] });
// ✓ 123, true, []
// ✗ "hello", null

// @TypeName: custom type reference
compiler.addPredefinedType('IPv4', /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/);
compiler.compile({ rule: '@IPv4' });
// ✓ "192.168.1.1"

// @TypeName(args): reference with arguments
compiler.addPredefinedType('trim_string', (v, min, max) =>
    typeof v === 'string' && v.trim().length >= min && v.trim().length <= max
);
compiler.compile({ rule: '@trim_string(2,16)' });
// ✓ "  hello  " (length is 5 after trimming)

8.2 Suffixes

Suffix formEquivalent formDescriptionExamples
type[]["$.list", type]Variable-length list (any length)"string[]"
"uint32[]"
type[N]["$.array", N, type]Fixed-length array (exactly N elements)"string[3]"
"int[5]"
type[N,M]["$.array", [N,M], type]Ranged-length array (N to M elements)"string[1,5]"
"uint[2,10]"
type[N,]["$.array", [N], type]Min-length array (at least N elements)"string[1,]"
"int[3,]"
type{}["$.map", type]Mapping (object value-type constraint)"string{}"
"uint32{}"

Examples

typescript
// type[]: variable-length list
compiler.compile({ rule: 'string[]' });
// ✓ [], ["a"], ["a", "b", "c"]

// type[N]: fixed-length array
compiler.compile({ rule: 'uint8[3]' });
// ✓ [1, 2, 3]
// ✗ [1, 2], [1, 2, 3, 4]

// type[N,M]: ranged-length array
compiler.compile({ rule: 'string[2,5]' });
// ✓ ["a", "b"], ["a", "b", "c", "d", "e"]
// ✗ ["a"], ["a", "b", "c", "d", "e", "f"]

// type[N,]: min-length array
compiler.compile({ rule: 'int[1,]' });
// ✓ [1], [1, 2, 3, ...]
// ✗ []

// type{}: mapping type
compiler.compile({ rule: 'number{}' });
// ✓ { a: 1, b: 2 }
// ✗ { a: 1, b: "2" }  // Values must all be numbers

8.3 Object key syntax sugar

Object key syntax sugar allows special suffixes on key names in object rules to simplify common structural patterns. These shorthands are high-frequency TypeGuard features and can significantly improve rule readability.

8.3.1 Syntax table

SugarEquivalent formDescriptionTypical usage
field?Optional fieldField may exist or notOptional config items, optional properties
field->[]["$.list", ...]Field value is a variable-length listTag arrays, comment lists
field->[N]["$.array", N, ...]Field value is a fixed-length arrayRGB color, coordinate point
field->[N,M]["$.array", [N,M], ...]Field value is a ranged-length arrayPaging results, batch data
field->[N,]["$.array", [N], ...]Field value is a min-length arrayLists with at least one item
field->{}["$.map", ...]Field value is a mapping objectKey-value config, dictionaries
field->()["$.strict", ...]Field value is strict (no extra keys)Exact-structure validation
field->(=)["$.equal", ...]Field value is recursively strictDeep structure validation

8.3.2 Optional field field?

typescript
// Optional age field
compiler.compile({
    rule: {
        name: 'string',
        age?: 'uint8'
    }
});
// ✓ { name: "alice" }
// ✓ { name: "bob", age: 25 }

// Multiple optional fields
compiler.compile({
    rule: {
        id: 'uint32',
        'email?': 'string',
        'phone?': 'string',
        'address?': 'string'
    }
});

8.3.3 List field field->[]

typescript
// Variable-length tag list
compiler.compile({
    rule: {
        title: 'string',
        'tags->[]': 'string'
    }
});
// ✓ { title: "Post", tags: [] }
// ✓ { title: "Post", tags: ["tech", "js"] }

// Object list
compiler.compile({
    rule: {
        'users->[]': {
            id: 'uint32',
            name: 'string'
        }
    }
});
// ✓ { users: [{ id: 1, name: "alice" }, { id: 2, name: "bob" }] }

8.3.4 Array field field->[N] / field->[N,M] / field->[N,]

typescript
// Fixed-length array: RGB color
compiler.compile({
    rule: {
        name: 'string',
        'color->[3]': 'uint8'
    }
});
// ✓ { name: "red", color: [255, 0, 0] }
// ✗ { name: "red", color: [255, 0] }

// Ranged-length array: 1–5 roles
compiler.compile({
    rule: {
        username: 'string',
        'roles->[1,5]': 'string'
    }
});
// ✓ { username: "admin", roles: ["admin", "moderator"] }
// ✗ { username: "admin", roles: [] }

// Min-length array: at least one skill
compiler.compile({
    rule: {
        name: 'string',
        'skills->[1,]': 'string'
    }
});
// ✓ { name: "alice", skills: ["js", "ts", "python"] }
// ✗ { name: "bob", skills: [] }

8.3.5 Mapping field field->{}

typescript
// String key-value pairs
compiler.compile({
    rule: {
        name: 'string',
        'metadata->{}': 'string'
    }
});
// ✓ { name: "config", metadata: { author: "alice", version: "1.0" } }
// ✗ { name: "config", metadata: { count: 123 } }  // Values must be strings

// Numeric config mapping
compiler.compile({
    rule: {
        'settings->{}': 'number'
    }
});
// ✓ { settings: { timeout: 3000, retries: 5 } }

8.3.6 Strict field field->() and field->(=)

typescript
// field->(): single-level strict match
compiler.compile({
    rule: {
        'config->()': {
            host: 'string',
            port: 'uint16'
        }
    }
});
// ✓ { config: { host: "localhost", port: 3000 } }
// ✗ { config: { host: "localhost", port: 3000, debug: true } }  // debug not allowed

// field->(=): recursive strict match
compiler.compile({
    rule: {
        'settings->(=)': {
            db: {
                host: 'string',
                port: 'uint16'
            }
        }
    }
});
// ✓ { settings: { db: { host: "localhost", port: 5432 } } }
// ✗ { settings: { db: { host: "localhost", port: 5432, pool: 10 } } }  // db cannot have extra keys

8.3.7 Combined usage

Multiple shorthands can be combined to build complex structures:

typescript
compiler.compile({
    rule: {
        id: 'uint32',
        title: 'string',
        'author?': 'string',                    // Optional
        'tags->[]': 'string',                   // Variable-length list
        'comments->[0,100]': {                  // At most 100 comments
            user: 'string',
            content: 'string',
            'replies->[0,10]': {                // At most 10 replies per comment
                user: 'string',
                content: 'string'
            }
        },
        'metadata->{}?': 'string',              // Optional string mapping
        'settings->()': {                       // Strict config object
            visible: 'boolean',
            pinned: 'boolean'
        }
    }
});

TIP

Object key syntax sugar can greatly reduce nesting and redundancy. It is a key technique for writing clear, maintainable rules. In practice, it is recommended to use these shorthands instead of the equivalent expanded forms whenever possible.