Type Statements
Type Statements
Type statements define custom types that can be used for concept declarations. This enables semantic typing and type aliases.
Tips
There are standard types defined in the std. library imports included with trilogy. These are suggested default integration targets, though users can create/extend their own.
Syntax
type_declaration: "type" IDENTIFIER (data_type | validated_type) ("|" data_type)* type_drop_clause?
type_drop_clause: "DROP" IDENTIFIER ("|" IDENTIFIER)*
Basic Type Alias
Create an alias for an existing type:
type currency float;
type email string;
type phone_number string;
Union Types
Define a type that can be one of several base types:
type identifier int | string;
Using Custom Types
Apply a custom type as a trait on its base type with base_type::trait:
type currency float;
type email string;
key customer_id int;
property customer_id.email string::email;
property customer_id.balance float::currency;
Semantic Types
Use types to add semantic meaning:
type usd float;
type eur float;
type percentage float;
property order_id.total float::usd;
property order_id.tax_rate float::percentage;
Value Ranges
Attach inclusive bounds to numeric, date, or datetime types. A constrained custom type carries its bounds through the ::trait application:
type percent float[0..100];
key measurement_id int;
properties measurement_id (
completion float::percent,
temperature float[-40.0..120.5],
quantity int[0..],
bucket int[0..10, 20..30],
exact int[42],
amount numeric(20,5)[0..],
observed_on date['2020-01-01'..'2030-12-31'],
);
| Form | Meaning |
|---|---|
int[0..100] | Between 0 and 100, inclusive |
int[0..] / int[..100] | Lower / upper bound only |
int[0..10, 20..30] | Either interval (comma means OR) |
int[42] | One permitted value |
Bounds belong on declarations, including parameters and function bindings; they are not a cast-time validation operation. A constrained custom type cannot also be a union of base types. Applying explicit bounds and a trait with conflicting bounds is an error.
Enumerations
Use enum<base>[...] for a finite set of string or integer values:
key ticket_id int;
property ticket_id.status enum<string>['new', 'active', 'closed'];
property ticket_id.priority enum<int>[1, 2, 3];
The enum retains its base type for compatible operations. Its declared members also let Trilogy reject impossible literal comparisons, such as status = 'deleted', before SQL execution. Warehouse validation checks the stored values against the enum. When ingest infers an enum, review whether the observed members describe the intended future domain.
String Patterns and Nullability
type product_code string['[A-Z]{3}[0-9]{4}'];
key product_id int;
property product_id.code string::product_code;
property product_id.optional_code string['[A-Z]{3}[0-9]{4}']?;
A string validator is a full-match regex, not a substring search. Python checks use Python regex syntax; SQL validation uses the target engine's regex dialect. Choose patterns supported by both. Constraints apply to non-null values; ? permits null independently.
When Constraints Are Checked
Parsing rejects provably impossible literal predicates and out-of-domain literal arguments to constrained function parameters. Datasource validation checks actual values, with full-source domain scans for ranges, enums, and patterns in addition to sampled type checks. Type declarations alone do not scan a warehouse or install database constraints.
See Typing and Validation for validation scopes, partial and complete bindings, assertions, and test workflows.
Use Cases
- Domain modeling
- Type safety
- Documentation through types
- Standardizing data formats
- Semantic layer enrichment
Example
# Define semantic types
type customer_id int;
type order_id int;
type usd float;
type email_address string;
# Use types in definitions
key cid int::customer_id;
key oid int::order_id;
property cid.email string::email_address;
property cid.balance float::usd;
property oid.total float::usd;
property oid.customer int::customer_id;
Tips
Custom types help document intent and can enable tooling to provide better validation, code completion - and rendering.
Trilogy studio relies heavily on them to provide better default visualizations.