Skip to content

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.

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.

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 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.

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",
});

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,
});

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,
},
},
});

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.

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,
});

The ?? operator is supported for providing default values, particularly useful in composite props.

timeout: props.timeout ?? 30,

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.

// EVL001: Expression not statically evaluable
export const store = new StorageType({
name: getName(), // function call — not a literal
});
// EVL002: Resource inside control flow
if (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.

// EVL003: Dynamic property access
const name = config[key];
// EVL004: Spread from non-const source
export const store = new StorageType({
...getDefaults(),
});
  • let/var bindings — only const is statically traceable
  • Class declarationsclass MyStore extends StorageType {...}
  • Template literals without known tags — only lexicon-registered tags
  • Computed property names{ [key]: value }
  • require() — only import statements
  • Top-level await
  • Decorators

For the full EVL rule reference with configuration options, see Evaluability Rules.

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.