# Type Semantics

## Narrow dynamic values

Allow broad values only at genuine boundaries such as JSON, reflection, SDKs, middleware, or framework entrypoints:

```text
dynamic input -> parse and validate -> explicit structure -> business logic
```

Prefer unknown-but-safe types before narrowing. Choose DTOs, records, dataclasses, TypedDict, schemas, unions, enums, protocols, constrained generics, aliases, or value objects according to the language. Do not create wrapper types that add no constraint or meaning.

Type annotations do not replace runtime validation for HTTP, messages, files, weak database fields, or third-party SDK data. Validate the relevant structure and semantic constraints before entering trusted business code.

## Define the meaning of broad values

A broad type is not limited to `any` or `number`. It includes values whose declared type leaves business-significant possibilities unresolved, such as free-form strings, nullable booleans, untyped collections, generic maps, unions without a reliable discriminator, and coarse serialized values.

For each broad value, determine only the dimensions relevant to its contract and risk:

- allowed value domain and variants;
- distinctions among missing, null, empty, zero, false, and defaulted values;
- accepted coercions, normalization, and rejection behavior;
- serialization and round-trip behavior;
- validation limits and error behavior.

Then add category-specific meaning where applicable. The following categories are prompts for analysis, not a closed taxonomy or a checklist that every value must satisfy:

- strings and identifiers: enum versus free text, format, encoding, normalization, case and whitespace rules, and length;
- booleans and state flags: two-state versus tri-state meaning, absence, defaults, and whether a richer enum is required;
- collections: item type, size, ordering, uniqueness, duplicate handling, and nesting limits;
- objects and maps: required and optional fields, allowed keys, additional-field behavior, key/value constraints, and shape evolution;
- unions or polymorphic values: discriminator, allowed variants, exhaustiveness, and unknown-variant behavior;
- dates, times, durations, measurements, and identifiers: format, timezone or unit, valid range, and comparison semantics.

Do not turn this list into ceremony for every local value. Prefer a business type, schema, alias, enum, or validation rule when it removes a real ambiguity; do not add a wrapper that merely renames the underlying type.

### Numeric values are one category

For a broad numeric value, consider:

- integer versus fractional values;
- signedness and range;
- unit;
- precision, rounding, and comparison;
- `NaN`, infinity, negative zero, and scientific notation behavior when relevant;
- distinctions among null, missing, and zero;
- serialization precision.

Use exact decimal or integer minor units for money and exact measurements unless the contract explicitly chooses binary floating point. When the type system cannot express the constraint, add runtime validation and documentation beside the contract.

## Contract documentation and boundary

Document business reasons and invariants the type system cannot express, such as units, rounding, normalization, enum meanings, or special protocol values. Do not narrate syntax. Keep public-interface documentation beside the DTO, message, configuration, or schema, and update it with the code.

Test the relevant valid, boundary, invalid, missing, null, empty, unknown-variant, collection, precision, and round-trip cases. If narrowing changes accepted input, normalization, serialization, errors, historical-data handling, or caller-visible behavior, surface it as an external contract impact. Do not choose compatibility, migration, rollout, or rollback policy in this reference.
