Repository navigation
feat(errors): implement Standard Schema type inference in error() - #84
Merged
Merged
Conversation
martyy-code
added a commit
that referenced
this pull request
Aug 12, 2026
…ntract Documents the choice to adopt @standard-schema/spec as the runtime validation contract in error(), the three options considered (single validator, unknown, Standard Schema), and the consequences (transitive spec dependency, InferFields<S> widening deviation from spec idiom, validator-specific bugs as typing oddities). Closes #80 (the documentation half of the inference work; #83 covered the implementation). Refs #84 (implementation PR).
This was referenced Aug 12, 2026
Issue #83: `error()` declared a `<const T extends Record<string, unknown>>` placeholder parameter that was never inferred from any source — the `fields: StandardSchemaV1` parameter was opaque, so `T` always fell back to the default `Record<string, never>` at every call site. The signature promised inference; the contract did not deliver it. Replace `T` with a schema-derived `S extends StandardSchemaV1 | undefined`. The new `InferFields<S>` helper in `types.ts` extracts the output type via `StandardSchemaV1.InferOutput<S>`, intersected with `Record<string, unknown>` to satisfy the `ErrorFactory<TFields>` constraint. When `fields` is omitted, `InferFields<undefined>` falls back to `Record<string, never>` (preserving the existing default behaviour). Consumers now receive a factory whose return type carries the schema's output shape — the boilerplate `error<{ ... }>(...)` annotation is no longer required. The trailing cast at the end of `error()` is preserved (single cast, single boundary, rule 0008 compliant) but the gap it bridges is narrower: the cast now sits only at the metadata-attachment boundary. `ErrorConfig` in `types.ts` is updated to mirror the public signature. Three new tests in `error.test.ts` prove the inference at compile time (the test bodies would fail to type-check if inference regressed). A typed-mock factory (`createTypedMockSchema`) is added next to the existing `createMockSchema`; the typed mock declares the schema's `types` field so `StandardSchemaV1.InferOutput<S>` propagates. Closes #83. 85/85 tests pass (82 → 85, three new inference regressions). Type-check clean. Lint clean (run from the package dir).
codewizdave
pushed a commit
that referenced
this pull request
Oct 7, 2026
…ntract Documents the choice to adopt @standard-schema/spec as the runtime validation contract in error(), the three options considered (single validator, unknown, Standard Schema), and the consequences (transitive spec dependency, InferFields<S> widening deviation from spec idiom, validator-specific bugs as typing oddities). Closes #80 (the documentation half of the inference work; #83 covered the implementation). Refs #84 (implementation PR).
codewizdave
force-pushed
the
feat/83-standard-schema-inference
branch
from
October 7, 2026 13:27
bb39389 to
7560001
Compare
…ntract Documents the choice to adopt @standard-schema/spec as the runtime validation contract in error(), the three options considered (single validator, unknown, Standard Schema), and the consequences (transitive spec dependency, InferFields<S> widening deviation from spec idiom, validator-specific bugs as typing oddities). Closes #80 (the documentation half of the inference work; #83 covered the implementation). Refs #84 (implementation PR).
codewizdave
force-pushed
the
feat/83-standard-schema-inference
branch
from
October 7, 2026 13:32
7560001 to
967b0e2
Compare
martyy-code
added a commit
that referenced
this pull request
Oct 7, 2026
#91) Applied as a single consolidated commit on top of staging (post-#84). Original PR #89 was a 6-commit stack; this collapses to the final state so the structural rewrite (class internals) lands cleanly without incremental rebase conflicts. - New private class ErrorInstanceImpl in internal/error-instance-impl.ts - New private class ErrorFactoryImpl in internal/error-factory-impl.ts - error() now returns a callable bound to an ErrorFactoryImpl - ErrorInstance extends Error at runtime; instance instanceof Error is true - Brand marker (ErrorInstanceBrand) set as readonly class property - FACTORY_SYMBOL consolidated in types.ts (single source of truth) - ErrorConfig renamed to ErrorFactoryConfig and now exported - Tests cover instance class, brand marker, and Error prototype Closes #88. Co-authored-by: T3 Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #83.
What
error()declared a<const T extends Record<string, unknown>>placeholder parameter that was never inferred from any source. Thefields: StandardSchemaV1parameter was opaque, soTalways fell back to the defaultRecord<string, never>at every call site. The signature promised inference; the contract did not deliver it.How
Twith a schema-derivedS extends StandardSchemaV1 | undefinedinerror.ts:76.InferFields<S>helper intypes.tsthat usesStandardSchemaV1.InferOutput<S>, intersected withRecord<string, unknown>to satisfy theErrorFactory<TFields>constraint. The intersection is transparent at the call site — the consumer sees the precise shape, not the intersection.fieldsis omitted,InferFields<undefined>falls back toRecord<string, never>(existing default behaviour preserved).ErrorConfigto mirror the public signature.return ErrorFactoryInstance as ErrorFactory<Fields>is preserved (single cast, single boundary, rule 0008 compliant) but the gap it bridges is narrower: the cast sits only at the metadata-attachment boundary, not at the type-parameter boundary.Consumer impact
Before:
After:
Verification
InferFields<S>regressed, the test bodies would fail to type-check.pnpm type-checkclean.pnpm lintclean (run from the package dir).Follow-up commit
This PR also includes
docs/engineering/architecture/decisions/0001-standard-schema-for-runtime-validation.md(ADR 0001) which captures the architectural rationale for the inference choice. Closes #80.Note
The pre-commit
eslint --fixhook at the monorepo root is still broken (noeslint.config.jsat root, only atpackages/errors/). I committed with--no-verifyafter confirming the code passes prettier + eslint from the package dir. Same flag as #82.🤖 Generated with Claude Code