Type safety primer

deet treats every model as a contract. The typechecker validates joins, aggregates, and nulls before SQL ships, so teams catch issues in code review instead of after a failed run.

The mental model

  • Sources declare the shape of data.
  • Models transform sources into new contracts.
  • deet check enforces types and nullability.

Types + nullability

If a column can be null, it is marked with ?. In the playground contract view, that becomes a TypeScript-style optional field:

email?: string;

What deet check catches

  • Mismatched join keys and filters
  • Aggregates on non-numeric columns
  • Missing columns or nullability violations

Example: fail fast, then fix

source users = table("users") : { id: i64, name: string, email: string? }

model broken = users
  |> filter(id == "oops")

id is numeric, so comparing it to a string trips the typechecker. Fix the predicate and the contract goes clean:

model fixed = users
  |> filter(id > 0)

Contract view

Every model surfaces as a contract that mirrors TypeScript interfaces:

interface users {
  id: number;
  name: string;
  email?: string;
}

Run it locally

deet check ./main.dt

Next steps