typeOf computes the type of an expression represented as an AST object. Rather than deriving types from DSL notation, it walks the expression tree from its leaves upward, assigning each subexpression a result type. The running rental-price expression uses amount and percentage as types, abbreviated in diagrams as $ and %. section 13.1
Figure 13.5 The type computation of the rental price expression, in terms of the AST
The AST view makes the bottom-up computation visible: each node’s type depends on the types computed for its child expressions. section 13.1
The function is recursive and dispatches on an AST object’s concept. An Attribute obtains its declared type from settings; an Attribute Reference follows its referenced attribute and computes that attribute’s type. Parentheses preserve the type of their contained expression. This lets callers ask for a type through representation details such as references and grouping. section 13.1
Starting the type-system module
Place type computation in src/language/type-system.js. Begin with a typeOfAttribute helper, then switch on concepts such as Attribute and Attribute Reference. Export typeOf so projections and constraints can reuse one definition of expression typing. section 13.1
Binary operations require more than simply returning a child type: the result depends on the operator and both operand types. With three known types and the operators “-” and “of,” there are 18 operand/operator combinations, many without an obvious valid result. A binary-operation helper returns an applicable type or untype when it cannot determine one. For “-”, number types are amount or percentage; for “of”, an amount right operand yields amount. section 13.1
typeOf distinguishes useful outcomes for callers:
- A concrete expression type, such as amount or percentage, can drive projection and compatibility checks. section 13.1
- untype represents an expression whose type is not determined by the available rules, allowing later checking to report a meaningful issue rather than assume validity. section 13.1
- Ancestors can be passed during recursion so a Number object’s type can be inferred from its containing context. section 13.1
Type computation becomes most valuable when constraints consume it. Instead of assuming an Attribute Reference is directly contained by an Attribute, a constraint computes the type of any Attribute value and compares it with the containing Attribute’s type. The same approach checks Binary Operation operands against their operator requirements and reports the computed types in errors. section 13.2
Checking a rental attribute value
For an Attribute with an AST value, compute the Attribute’s declared type and compute the value’s type with the Attribute added to the ancestor list. If the two differ, report that the Attribute and its value must match, including both computed types. This works for every expression-shaped value, not only direct Attribute References. section 13.2
