TypeScript as Data
chant reads your TypeScript source files as structured data. It understands a specific subset of the language — the part that can be evaluated statically without executing code. This page documents exactly which patterns are supported and which are not.
Supported Patterns
Section titled “Supported Patterns”Resource declarations
Section titled “Resource declarations”The core pattern: export a const binding initialized with a typed resource constructor.
import { StorageType } from "@intentius/chant-lexicon-<name>";
export const store = new StorageType({ name: "my-data", versioned: true,});The evaluator extracts the export name (store), resolves the resource type from the lexicon, and evaluates the constructor argument.
Literal values
Section titled “Literal values”Strings, numbers, booleans, null, arrays, and nested object literals are evaluated directly.
import { ServiceType } from "@intentius/chant-lexicon-<name>";
export const service = new ServiceType({ name: "handler", timeout: 30, memorySize: 128, environment: { variables: { DEBUG: "false", }, },});Const variable references
Section titled “Const variable references”const bindings with literal initializers can be referenced by name. The evaluator traces the reference to its initializer and evaluates it.
import { StorageType } from "@intentius/chant-lexicon-<name>";
const tags = { project: "myapp", env: "prod" };
export const store = new StorageType({ name: "data", tags: tags,});Only const bindings are supported. let and var are mutable and cannot be statically traced.
Spread from const sources
Section titled “Spread from const sources”Object spread is supported when the source is a const binding with a known literal value.
import { ServiceType } from "@intentius/chant-lexicon-<name>";
const defaults = { timeout: 30, memorySize: 128 };
export const service = new ServiceType({ ...defaults, name: "handler",});Import and re-export
Section titled “Import and re-export”Standard import statements are followed through the module graph. The evaluator resolves imported names to their definitions in other files.
import { StorageType } from "@intentius/chant-lexicon-<name>";import { sharedTags } from "./shared";
export const store = new StorageType({ name: "data", tags: sharedTags,});Cross-file resource references
Section titled “Cross-file resource references”Import a resource from another file and access its attributes. The evaluator resolves the attribute through the lexicon’s attribute registry.
import { ServiceType } from "@intentius/chant-lexicon-<name>";import { dataTable } from "./table";
export const service = new ServiceType({ environment: { variables: { TABLE_ARN: dataTable.arn, }, },});Intrinsic tagged templates
Section titled “Intrinsic tagged templates”Lexicons can register tagged template literals for provider-specific intrinsics (e.g., string substitution with deploy-time values):
import { StorageType, Intrinsic, Params } from "@intentius/chant-lexicon-<name>";
export const store = new StorageType({ name: Intrinsic`${Params.StackName}-data`,});See your lexicon’s documentation for available intrinsics.
Typed property-kind constructors
Section titled “Typed property-kind constructors”Sub-resource properties can use typed constructors for autocompletion and validation:
import { EncryptionConfig, AccessConfig } from "@intentius/chant-lexicon-<name>";
export const config = new EncryptionConfig({ algorithm: "AES256",});
export const access = new AccessConfig({ publicAccess: false,});Nullish coalescing for defaults
Section titled “Nullish coalescing for defaults”The ?? operator is supported for providing default values, particularly useful in composite props.
timeout: props.timeout ?? 30,Unsupported Patterns
Section titled “Unsupported Patterns”The following patterns are caught by the evaluability lint rules (EVL) before the evaluator runs. Your editor shows these as lint errors, and chant lint reports them in CI.
Function calls as values
Section titled “Function calls as values”// EVL001: Expression not statically evaluableexport const store = new StorageType({ name: getName(), // function call — not a literal});Control flow around resources
Section titled “Control flow around resources”// EVL002: Resource inside control flowif (env === "prod") { export const store = new StorageType({...});}Resources must be top-level exports. For environment-specific configuration, use separate files or composites with different prop values.
Dynamic property access
Section titled “Dynamic property access”// EVL003: Dynamic property accessconst name = config[key];Spread from dynamic sources
Section titled “Spread from dynamic sources”// EVL004: Spread from non-const sourceexport const store = new StorageType({ ...getDefaults(),});Other unsupported patterns
Section titled “Other unsupported patterns”let/varbindings — onlyconstis statically traceable- Class declarations —
class MyStore extends StorageType {...} - Template literals without known tags — only lexicon-registered tags
- Computed property names —
{ [key]: value } require()— onlyimportstatements- Top-level
await - Decorators
For the full EVL rule reference with configuration options, see Evaluability Rules.
Why These Constraints?
Section titled “Why These Constraints?”The supported subset is the set of TypeScript patterns that can be evaluated without executing code — patterns where every value is either a literal, a reference to a known constant, or a symbolic cross-resource reference.
This makes the evaluator fast (no interpretation overhead), deterministic (same source always produces the same output), and auditable (every output value traces to a specific line in the source).
The static boundary also defines a natural division of labor with agentic workflows: agents resolve dynamic values (looking up VPC IDs, reading secrets, querying state), then write or populate chant source files. chant synthesizes the deterministic structure. Agents handle what changes; chant handles what shouldn’t.