Schema.Class / TaggedClass / TaggedErrorClass for any reusable model, union member, or error | a bare Schema.Struct for a domain type — Struct is for throwaway inline shapes |
X.make({...}) as the default constructor — EXCEPT TaggedErrorClass, where failing with the yieldable yield* new SomeError({...}) is the house idiom (glob, workspaces, walker all construct errors with new) | new X({...}) for models outside a measured hot path (both validate identically) |
reach for { disableChecks: true } only to accept trusted data that would fail a .check(...) | reach for it as a speed switch — despite a docstring promising to "skip validation", it gates only the check phase: type errors still throw, the structural re-parse still runs, and a depth-20 build measured 2671 ms with it vs 2711 ms without |
build a recursive Schema.Class AST via an internal Object.assign(Object.create(Proto), props) path, validating once at the boundary | construct a recursive Schema.Class tree node-by-node — each level re-validates its whole subtree, so cost doubles per level (depth 20 = 2.7 s, probed beta.97); the bypass is faithful because Data.Class's constructor is Object.assign(this, props) |
dodge the class factory's reserved static names when designing domain statics — every Schema.Class/TaggedClass/TaggedErrorClass base already declares identifier, fields, ast, pipe, rebuild, make, makeOption, makeEffect, annotate, annotateKey, check, extend, mapFields (vendored Schema.ts makeClass) | a domain static reusing one of those names — an incompatible signature is a TS2417 compile error (static side incorrectly extends base); the lockfiles port had to rename an approved LockfileIntegrity.check(lockfile, manifests) design to compare on exactly this |
| conditional-spread an absent optional field | pass an explicit undefined for a Schema.optionalKey — a present undefined throws |
Schema.optionalKey for object fields | Schema.optional unless the value itself must carry undefined |
.check(is*) to constrain, refine to narrow, check(makeFilter(...)) for cross-field | the removed positive/negative or the v3 filter/greaterThan names |
tagged unions of TaggedClass members (_tag branching) | untagged unions for domain variants |
Schema.Literals(["a", "b", "c"]) for any multi-literal union (reason fields, enums) | the v3 variadic Schema.Literal("a", "b", "c") — v4 Literal takes ONE argument; tsgo rejects the variadic call (TS2554), but the runtime silently keeps only the first literal, so a suite run before typecheck green-lights a schema that rejects every other member |
Source.pipe(decodeTo(Target, SchemaTransformation.transform({...}))) | a top-level Schema.transform / transformOrFail — not callable in beta.94 |
pin transformOrFail's type params explicitly when a union codec's members carry instance methods — SchemaTransformation.transformOrFail<(typeof Classified)["Encoded"], string>({...}) | relying on inference after adding an instance method to a Schema.TaggedClass union member — transformOrFail unifies one T from decode-out and encode-in, and decodeTo pins both to the union's Encoded side, which no longer satisfies the method-bearing instance type; the existing codec breaks at the declaration site (hit on beta.98 adding a method to a DependencySpecifier.FromString member) |
a FromString Schema.Codec<Self, string> static (string = the encoded form of the same schema) | a second parser divorced from the schema |
cause: Schema.Defect() on an error class | cause: Schema.Defect — the bare (uncalled) form throws at construction |
Schema.decodeUnknownEffect / encodeUnknownEffect in Effect flows | *Sync outside a genuine sync boundary |
Schema.DurationFromMillis / Schema.DateTimeUtcFromString (composed with Schema.fromJsonString for byte stores) when the value must serialize | Schema.Duration / Schema.DateTimeUtc in a persisted or wire schema — both are declare schemas with no JSON encoding (Schema.ts:10575,11972), so they round-trip in memory and fail at the serialization boundary; the ts-vfs cache metadata hit exactly this |
annotate recursive Schema.suspend refs Schema.Codec<T> (services default never) | Schema.Schema<T> as the suspend annotation — it compiles at the declaration but leaves DecodingServices unknown, so every decode entrypoint rejects the schema (unknown is not assignable to never, probed beta.94); a schema nobody decodes directly hides the trap until a consumer tries |
derive variants via mapFields(Struct.pick/omit/map(...)) | duplicate a schema to re-encode the same data |
attach brand statics with Object.assign; export the type as string & Brand.Brand<"N"> | try to merge a namespace into the brand const (impossible) |
override BOTH [Equal.symbol] AND [Hash.symbol] when equality ignores fields | override [Equal.symbol] alone — the hash fast-path silently defeats it |
Schema.toJsonSchemaDocument(S) | Schema.toJsonSchema(S) — that export does not exist. It returns { dialect, schema, definitions }, not $defs / properties |
a single-return ternary chain in an error's message getter | an exhaustive switch with no terminal return — tsgo accepts it, but Biome's useGetterReturn rejects it (see below) |