/** * Type-level tests for RequireDefaults and related utilities. * * These tests verify compile-time behavior of RequireDefaults, * StripDefaultBrand, and the Default brand detection logic. * If any type assertion is wrong, this file will fail to compile. */ import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import type { RequireDefaults, StripDefaultBrand, } from "../src/builder/types.ts"; import type { Default } from "@commontools/api"; import type { Cell } from "@commontools/runner"; // ============================================================================ // Helpers // ============================================================================ /** * Asserts T and U are mutually assignable (structurally equal). * Produces `never` on mismatch → compile error at call site. */ type AssertEqual = [T] extends [U] ? [U] extends [T] ? true : never : never; type MustBeTrue = T; /** * Flattens an intersection type to a plain object type so that * AssertEqual works correctly with RequireDefaults (which is an * intersection of two mapped types). */ type Simplify = { [K in keyof T]: T[K] }; /** * Asserts T and U are NOT mutually assignable. * Produces `never` if they ARE equal → compile error at call site. */ type AssertNotEqual = [T] extends [U] ? [U] extends [T] ? never : true : true; // ============================================================================ // StripDefaultBrand — non-Default types are unchanged // ============================================================================ const _stripPlainString: MustBeTrue< AssertEqual, string> > = true; const _stripPlainNumber: MustBeTrue< AssertEqual, number> > = true; const _stripPlainObject: MustBeTrue< AssertEqual, { a: string }> > = true; // ============================================================================ // StripDefaultBrand — Default strips to plain T // ============================================================================ const _stripDefaultString: MustBeTrue< AssertEqual>, string> > = true; const _stripDefaultNumber: MustBeTrue< AssertEqual>, number> > = true; const _stripDefaultBoolean: MustBeTrue< AssertEqual>, boolean> > = true; // Default strips to T|undefined (brand removed, undefined kept) const _stripDefaultWithUndefined: MustBeTrue< AssertEqual< StripDefaultBrand>, string | undefined > > = true; // ============================================================================ // RequireDefaults — plain optional fields are unchanged // ============================================================================ const _plainOptionalPreserved: MustBeTrue< AssertEqual< Simplify>, { name?: string | undefined } > > = true; const _plainRequiredPreserved: MustBeTrue< AssertEqual< Simplify>, { name: string } > > = true; // ============================================================================ // RequireDefaults — Default<> fields become required with brand stripped // ============================================================================ const _stringDefaultRequired: MustBeTrue< AssertEqual< Simplify }>>, { title: string } > > = true; const _numberDefaultRequired: MustBeTrue< AssertEqual< Simplify }>>, { count: number } > > = true; const _booleanDefaultRequired: MustBeTrue< AssertEqual< Simplify }>>, { enabled: boolean } > > = true; const _objectDefaultRequired: MustBeTrue< AssertEqual< Simplify }>>, { options: { x: number } } > > = true; // ============================================================================ // RequireDefaults — mixed: Default fields required, plain fields preserved // ============================================================================ type Mixed = { title?: Default; count?: Default; name?: string; id: number; }; const _mixed: MustBeTrue< AssertEqual< Simplify>, { title: string; count: number; name?: string | undefined; id: number } > > = true; // ============================================================================ // RequireDefaults — Default // The implementation strips `| undefined` via Exclude when making the key // required, so the value type becomes just T (not T|undefined). // ============================================================================ const _undefinableDefault: MustBeTrue< AssertEqual< Simplify }>>, { tag: string } > > = true; // ============================================================================ // RequireDefaults — Cell-wrapped Default fields // ============================================================================ const _cellDefaultRequired: MustBeTrue< AssertEqual< Simplify, never> }>>, { items: Cell } > > = true; // ============================================================================ // RequireDefaults — Default field in a union with a plain type // The presence of a Default-branded member in the union makes the field required. // ============================================================================ const _unionDefault: MustBeTrue< AssertEqual< Simplify | number }>>, { value: string | number } > > = true; // ============================================================================ // RequireDefaults — plain union (no Default) stays optional // ============================================================================ const _plainUnionPreserved: MustBeTrue< AssertEqual< Simplify>, { value?: string | number | undefined } > > = true; // ============================================================================ // RequireDefaults — is only one level deep (inner Default fields are not // processed, preserving the Default brand on nested types) // ============================================================================ type Nested = { outer?: Default; inner: { a?: Default }; }; const _shallowOnly: MustBeTrue< AssertEqual< Simplify>, // `outer` is made required; `inner` is untouched (Default brand preserved inside) { outer: string; inner: { a?: Default } } > > = true; // ============================================================================ // StripDefaultBrand — any, never, unknown pass through unchanged // ============================================================================ const _stripAny: MustBeTrue, any>> = true; const _stripNever: MustBeTrue, never>> = true; const _stripUnknown: MustBeTrue< AssertEqual, unknown> > = true; // ============================================================================ // StripDefaultBrand — array and tuple types // ============================================================================ // Plain array is unchanged const _stripPlainArrayType: MustBeTrue< AssertEqual, string[]> > = true; // Default strips to string[] const _stripDefaultArrayType: MustBeTrue< AssertEqual>, string[]> > = true; // Default<[string, number], ["a", 0]> strips to the tuple type const _stripDefaultTuple: MustBeTrue< AssertEqual< StripDefaultBrand>, [string, number] > > = true; // ============================================================================ // StripDefaultBrand — Default intersection // ============================================================================ type IntersectedT = { a: string } & { b: number }; const _stripDefaultIntersection: MustBeTrue< AssertEqual< StripDefaultBrand>, IntersectedT > > = true; // ============================================================================ // RequireDefaults — any, never, unknown // ============================================================================ // any absorbs all intersections: RequireDefaults = any const _requireDefaultsAny: MustBeTrue< AssertEqual, any> > = true; // A field typed as `never` has no Default brand; it is preserved as-is const _neverFieldPreserved: MustBeTrue< AssertEqual>, { x: never }> > = true; // keyof unknown = never → RequireDefaults has no keys const _requireDefaultsUnknownHasNoKeys: MustBeTrue< AssertEqual>, never> > = true; // ============================================================================ // RequireDefaults — empty object // ============================================================================ // RequireDefaults on an empty object produces a type with no keys // (uses `Record` to avoid the `ban-types` lint rule for literal `{}`) type _EmptySchema = Record; const _emptyObjectHasNoKeys: MustBeTrue< AssertEqual>, never> > = true; // ============================================================================ // RequireDefaults — array and tuple types as Default fields // ============================================================================ const _arrayDefault: MustBeTrue< AssertEqual< Simplify }>>, { items: string[] } > > = true; const _tupleDefault: MustBeTrue< AssertEqual< Simplify }>>, { pair: [string, number] } > > = true; // ============================================================================ // RequireDefaults — index signatures // ============================================================================ // Plain index signature is preserved unchanged const _plainIndexSig: MustBeTrue< AssertEqual< Simplify>, { [key: string]: string } > > = true; // Default-branded index signature has brand stripped to plain value type const _defaultIndexSig: MustBeTrue< AssertEqual< Simplify }>>, { [key: string]: string } > > = true; // ============================================================================ // RequireDefaults — Default intersection field // ============================================================================ const _intersectionDefault: MustBeTrue< AssertEqual< Simplify }>>, { x: IntersectedT } > > = true; // ============================================================================ // RequireDefaults — Default in generic context // ============================================================================ // RequireDefaults works correctly when used inside a generic type alias type ApplyRequireDefaults = Simplify>; const _genericContext: MustBeTrue< AssertEqual< ApplyRequireDefaults<{ name?: Default; id: number }>, { name: string; id: number } > > = true; // ============================================================================ // RequireDefaults — `any` fields are not treated as Default-branded // ============================================================================ // IsDefaultField = false (IsAny guard), so `any` fields remain unchanged. // RequireDefaults must not make an `any`-typed field required. // Note: we can't assert the full shape with `any` since `any` swallows // structural equality — instead we verify the key remains optional. type _AnyFieldResult = Simplify>; const _anyFieldOptional: MustBeTrue< AssertEqual > = true; // ============================================================================ // Negative tests — things that must NOT happen // ============================================================================ // Plain optional field must NOT be made required const _plainOptionalNotRequired: MustBeTrue< AssertNotEqual< Simplify>, { name: string } > > = true; // Default-branded field must NOT remain optional after RequireDefaults const _defaultFieldNotOptional: MustBeTrue< AssertNotEqual< Simplify }>>, { name?: string } > > = true; // StripDefaultBrand must NOT erase a Default<> type to never const _stripDefaultNotNever: MustBeTrue< AssertNotEqual>, never> > = true; // StripDefaultBrand must NOT erase a plain type to never const _stripPlainNotNever: MustBeTrue< AssertNotEqual, never> > = true; // ============================================================================ // Runtime stub — the type assertions above are the real tests // ============================================================================ describe("RequireDefaults type-level tests", () => { it("all type assertions compile correctly", () => { // If this file compiled without errors, all static type assertions passed. expect(true).toBe(true); }); });