Skip to content

feat(schema): export schema types, GetSchema<T>, ValidateReturn, and rule types#55

Merged
SrHenry merged 15 commits into
developerfrom
feat/export-schema-types
Jun 5, 2026
Merged

feat(schema): export schema types, GetSchema<T>, ValidateReturn, and rule types#55
SrHenry merged 15 commits into
developerfrom
feat/export-schema-types

Conversation

@SrHenry

@SrHenry SrHenry commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Extract 11 inline schema type definitions from builder files into dedicated type files under src/validators/schema/types/ (BooleanSchema, NullSchema, UndefinedSchema, SymbolSchema, AnySchema, PrimitiveSchema, EnumSchema, ObjectSchema, TupleSchema, UnionSchema, IntersectionSchema)
  • Add GetSchema<T> recursive type helper that maps TypeScript types to their corresponding FluentSchema result types
  • Re-export all new schema types, ValidateReturn, and rule types (Custom, CustomFactory, Rule, CreateRuleArgs) from the types hub
  • Replace export * from ./v3/index.ts wildcard with explicit allowlist to prevent internal V3 composition types from leaking into the public API
  • Replace export * from ./types/index.ts wildcard in rules barrel with explicit type allowlist to prevent CUSTOM_RULE_BRAND (runtime symbol) and RuleStruct (internal dispatch type) from reaching the public API
  • Remove Optionalize<T>, GetSchemaStruct<T>, and RuleStruct<Rule> from public exports (internal-only, no external consumers)
  • Fix GetSchema edge cases: any guard, unknown/void/never branches, Record detection for all key types

Exports now available from @srhenry/type-utils

Type Description
GetSchema<T> Maps TS type → FluentSchema result type
ValidateReturn<T> Return type of schema validation
BooleanSchema, NullSchema, UndefinedSchema, SymbolSchema, AnySchema, PrimitiveSchema Simple schema builder types
EnumSchema, ObjectSchema, TupleSchema, UnionSchema, IntersectionSchema Complex schema builder types
TupleSchemaEntry, UnionSchemaEntry, IntersectionSchemaEntry Entry types for composite schemas
GetUnionEntryTypes, GetIntersectionEntryTypes Helper types for union/intersection entry unwrapping
Custom, CustomFactory, Rule, CreateRuleArgs Rule types
V1, V2, V3 Historical version namespaces
BaseStruct, TUnion, TIntersection, TypeGuardTupleUnwrap V3 flat exports (explicit allowlist)
GenericStruct, ObjectStruct, Struct, StructType Top-level V3 aliases (backward compat)

Key change: V3 wildcard → explicit allowlist

The barrel file src/validators/schema/types/index.ts previously used export * from './v3/index.ts', which re-exported every type inside the V3 namespace flat — including ~25 internal composition types that consumers should never import directly:

RequiredPartialStruct, OptionalPartialStruct, RequirefyStruct, OptionalizeStruct, WithRulesStruct, MapToStructs, ExactExtends, IsExactExtension, ObjectTree, ClassInstanceRef, ArrayEntries, RecordMetadata, TupleMetadata, TupleToTypeGuardMap, TupleToStructMap, AsPrimitiveStruct, FromUnionStruct, FromIntersectionStruct, FromRecordStruct, FromClassInstanceStruct, FromTupleStruct

These types are still accessible to internal consumers via the V3 namespace (e.g. V3.RequiredPartialStruct), but they are no longer re-exported as bare names from the barrel. Only the 4 top-level flat exports from v3/index.ts are explicitly allowed through: BaseStruct, TUnion, TIntersection, TypeGuardTupleUnwrap.

Note: The V3 concrete struct types (V3.StringStruct, V3.NumberStruct, etc.) were never available as bare flat imports — they were always inside the V3 namespace on the original developer branch. This PR does not change that.

Key change: Rules barrel wildcard → explicit allowlist

Similarly, src/validators/rules/index.ts previously used export * from ./types/index.ts, which re-exported CUSTOM_RULE_BRAND (a runtime unique symbol used internally for type branding) and RuleStruct<Rule> (an internal dispatch type). These are now excluded from the public API by replacing the wildcard with an explicit type allowlist. Internal consumers still import these directly from ./types/index.ts.

Key change: GetSchema edge cases

  • GetSchema<any>: Previously mis-mapped to FluentSchema<string, StringSchemaRules> because any distributes through conditionals. Now uses the 0 extends (1 & T) guard pattern to detect any early, correctly resolving to FluentSchema<any>.
  • GetSchema<unknown>: Previously returned never because unknown fails all conditional branches. Now has an explicit branch mapping to FluentSchema<any>.
  • GetSchema<void>: Added explicit branch mapping to FluentSchema<any>. Previously fell through to never.
  • GetSchema<never>: Explicitly returns never — intentional, since never inputs should produce never outputs.
  • GetSchema<Record<K, V>>: Previously used PropertyKey extends keyof T which never matched any Record type because symbol doesn't extend string or number. All Records silently fell through to the "specific object" branch, losing RecordSchemaRules (including .nonEmpty()). Now uses per-key-type checks: string extends keyof T, number extends keyof T, symbol extends keyof T — correctly detecting Records with any broad key type.
  • Unreachable never fallbacks: The broad string/number detection branches ([string] extends [T], [number] extends [T]) have documented never fallbacks for cases where [K] extends [T] is true but T extends K is false. These are unreachable because the only such types are any and unknown, both caught earlier, but the fallbacks are safety nets documented for future maintainers.

Test Plan

  • yarn tsc -p tsconfig.json --noEmit — all type assertions pass
  • yarn tsc -p tsconfig.esm.json --noEmit — ESM typecheck passes
  • yarn tsc -p tsconfig.cjs.json --noEmit — CJS typecheck passes
  • yarn test — 382 passed, 1 skipped
  • yarn build — ESM + CJS outputs generated
  • yarn check:fix — lint + format clean
  • yarn circular-dependencies — 0 circular deps
  • All new types importable from @srhenry/type-utils entry
  • GetSchema type-level assertions compile correctly (all cases including any, unknown, void, never, Record<string,V>, Record<number,V>, Record<symbol,V>, function, Date)
  • Strict Record assertions verify RecordSchemaRules is present (not {}), ensuring .nonEmpty() method is available
  • Internal V3 types no longer leak as flat exports
  • CUSTOM_RULE_BRAND and RuleStruct no longer in public API

SrHenry added 12 commits June 1, 2026 23:31
…ports

- Extract V3 namespace and BaseStruct to types/v3/index.ts
- Add top-level re-exports of TUnion, TIntersection, TypeGuardTupleUnwrap
- Redirect all V3 imports from types/index.ts to types/v3/index.ts
- Redirect V3Helpers imports to v3/index.ts in UnionSchema, IntersectionSchema, TupleSchema
- Remove local UnionSchema/TupleSchema type defs from or.ts/tuple.ts
- Fix ValidateReturn import paths in FluentSchema and FluentOptionalSchema
- Add canonical ValidateReturn.ts in validators/types/
- Re-export ValidateReturn from SchemaValidator.ts
- Zero circular dependencies confirmed
- V1 extracted to v1/index.ts with own BaseStruct/BaseTypes
- V2 extracted to v2/index.ts with own BaseStruct/BaseTypes
- V1 kept only original types (Struct<T,U>, ArrayStruct<T,U>, etc.)
- V3 convenience aliases removed from V1 (use V3.* directly)
- types/index.ts imports V1/V2 via `import type * as`, re-exports them
- Added top-level GenericStruct/ObjectStruct/Struct/StructType aliases to V3
  for backward compat with consumers importing bare names
- V1/index.ts: remove PrimitiveStruct/AnyStruct/etc that were V3 aliases
  in original, not part of actual V1 namespace signature
- types/index.ts: remove commented-out GetStruct implementation
- FluentSchema.ts: remove commented-out import and AppendMessageMethod
- GetSchema.spec.ts: strengthen type assertions with Assert wrappers
  for string literal, number literal, object, and optional object cases
  (prevents silent never regression)
Replace `export * from './v3/index.ts'` with an explicit allowlist of
4 flat V3 exports (BaseStruct, TUnion, TIntersection, TypeGuardTupleUnwrap).
The wildcard leaked ~25 internal composition types into the public API
(RequiredPartialStruct, OptionalPartialStruct, WithRulesStruct, MapToStructs,
ExactExtends, IsExactExtension, ObjectTree, ClassInstanceRef, ArrayEntries,
RecordMetadata, TupleMetadata, TupleToTypeGuardMap, TupleToStructMap,
AsPrimitiveStruct, FromUnionStruct, FromIntersectionStruct, FromRecordStruct,
FromClassInstanceStruct, FromTupleStruct). These remain accessible via
the V3 namespace (V3.RequiredPartialStruct etc.) for internal consumers
but are no longer re-exported flat.

Also removed from public API surface:
- Optionalize<T> — internal-only, no external consumers
- GetSchemaStruct<T> — internal-only, no external consumers
- RuleStruct<Rule> — internal dispatch type, not useful for consumers
  (test import redirected to rules/types/index.ts directly)
- CUSTOM_RULE_BRAND — was already removed in prior commit; this cleans
  up the remaining export line
…a edge cases

- Replace `export * from './types/index.ts'` in rules barrel with
  explicit type allowlist, preventing CUSTOM_RULE_BRAND (internal
  runtime symbol) and RuleStruct (internal dispatch type) from
  reaching the public API. Internal consumers still import these
  directly from ./types/index.ts.

- Add `any` guard to GetSchema using `0 extends (1 & T)` pattern
  to prevent `any` from distributing through the string branch.
  GetSchema<any> now correctly resolves to FluentSchema<any>.

- Add explicit `unknown` branch: GetSchema<unknown> resolves to
  FluentSchema<any> instead of never.

- Broaden Record detection from `string extends keyof T` to
  `PropertyKey extends keyof T`, so Record<number, V> and
  Record<symbol, V> are correctly classified as records rather than
  specific objects.

- Add type-level test assertions for GetSchema<any>,
  GetSchema<unknown>, and GetSchema<Record<number, string>>.
- NEVER disable GPG commit signing (no -c commit.gpgsign=false,
  --no-gpg-sign, or other bypasses). Stop and wait for user input.
- NEVER continue past GPG pinentry failures. Stop all work
  immediately and wait for user direction.
@SrHenry SrHenry force-pushed the feat/export-schema-types branch from 70f41b8 to dbd58ba Compare June 4, 2026 20:59
SrHenry added 3 commits June 4, 2026 19:20
…nches

PropertyKey extends keyof T never matched any Record because
symbol doesn't extend string/number. Replace with per-key-type
checks: string extends keyof T | number extends keyof T |
symbol extends keyof T. Add void/never input branches, document
unreachable never fallbacks, strengthen test assertions to strict
RecordSchemaRules (not any), add Record<symbol,V>, void, never,
function, Date test coverage.
…y array branch

BooleanSchema previously returned FluentSchema<any> — a type-level
mistake that masked a match test discriminated union issue. Fix
BooleanSchema to FluentSchema<boolean> and widen match test Input
type's discriminant from true to boolean.

Add readonly array branch to GetSchema (T extends readonly (infer U)[])
before mutable array check, since readonly U[] doesn't extend U[].
Add readonly string[] test, fix JSDoc object examples to show
Sanitize<T>, document RuleStruct internal import in test.
@SrHenry SrHenry merged commit 151222d into developer Jun 5, 2026
3 checks passed
@SrHenry SrHenry deleted the feat/export-schema-types branch June 5, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant