UNPKG

n4s

Version:

typed schema validation version of enforce

202 lines (163 loc) 5.7 kB
import { describe, expect, it } from 'vitest'; import { enforce } from '../../n4s'; // This test suite verifies that compound and schema rule types are properly defined // and can be used for type inference via the .infer property describe('Compound and Schema Rule Types', () => { it('should properly type allOf rules', () => { const rule = enforce.allOf( enforce.isNumber(), enforce.isNumber().greaterThan(0), ); type InferredType = typeof rule.infer; const value: InferredType = 5; void value; // Type test: string is not assignable to number const badValue: InferredType = 'test'; void badValue; expect(rule.run(5).pass).toBe(true); expect(rule.run(-1).pass).toBe(false); }); it('should properly type anyOf rules', () => { const rule = enforce.anyOf(enforce.isString(), enforce.isNumber()); type InferredType = typeof rule.infer; const str: InferredType = 'test'; const num: InferredType = 123; void str; void num; // Type test: boolean is not string | number // @ts-expect-error - boolean is not string | number const badValue: InferredType = true; void badValue; expect(rule.run('test').pass).toBe(true); expect(rule.run(123).pass).toBe(true); // @ts-expect-error - boolean is not string | number expect(rule.run(true).pass).toBe(false); }); it('should properly type noneOf rules', () => { const rule = enforce.noneOf(enforce.isString()); type InferredType = typeof rule.infer; const value: InferredType = 'test'; void value; expect(rule.run(123).pass).toBe(true); expect(rule.run('test').pass).toBe(false); }); it('should properly type oneOf rules', () => { const rule = enforce.oneOf( enforce.isString(), enforce.isNumber(), enforce.isBoolean(), ); type InferredType = typeof rule.infer; const str: InferredType = 'test'; const num: InferredType = 123; const bool: InferredType = true; void str; void num; void bool; expect(rule.run('test').pass).toBe(true); expect(rule.run(123).pass).toBe(true); }); it('should properly type optional rules', () => { const rule = enforce.optional(enforce.isString()); type InferredType = typeof rule.infer; const str: InferredType = 'test'; const undef: InferredType = undefined; const nul: InferredType = null; void str; void undef; void nul; // Type test: number is not string | undefined | null // @ts-expect-error - number is not string | undefined | null const badValue: InferredType = 123; void badValue; expect(rule.run('test').pass).toBe(true); expect(rule.run(undefined).pass).toBe(true); expect(rule.run(null).pass).toBe(true); // @ts-expect-error - number is not string | undefined | null expect(rule.run(123).pass).toBe(false); }); it('should properly type isArrayOf rules', () => { const rule = enforce.isArrayOf(enforce.isString()); type InferredType = typeof rule.infer; const arr: InferredType = ['a', 'b', 'c']; void arr; // Type test: number[] is not string[] // @ts-expect-error - number is not assignable to string const badArr: InferredType = [1, 2, 3]; void badArr; expect(rule.run(['a', 'b']).pass).toBe(true); // @ts-expect-error - number is not assignable to string expect(rule.run([1, 2]).pass).toBe(false); }); it('should properly type shape rules', () => { const rule = enforce.shape({ name: enforce.isString(), age: enforce.isNumber(), }); type InferredType = typeof rule.infer; const obj: InferredType = { name: 'John', age: 30 }; void obj; // Type test: age must be number // @ts-expect-error - age must be number const badObj: InferredType = { name: 'John', age: 'thirty' }; void badObj; expect(rule.run({ name: 'John', age: 30 }).pass).toBe(true); // @ts-expect-error - age must be number expect(rule.run({ name: 'John', age: 'thirty' }).pass).toBe(false); }); it('should properly type loose rules', () => { const rule = enforce.loose({ name: enforce.isString(), }); type InferredType = typeof rule.infer; const obj: InferredType = { name: 'John', extraProp: true }; void obj; expect(rule.run({ name: 'John', extraProp: true }).pass).toBe(true); }); it('should properly type partial rules', () => { const rule = enforce.partial({ name: enforce.isString(), age: enforce.isNumber(), }); type InferredType = typeof rule.infer; const obj1: InferredType = { name: 'John' }; const obj2: InferredType = { age: 30 }; const obj3: InferredType = {}; void obj1; void obj2; void obj3; expect(rule.run({ name: 'John' }).pass).toBe(true); expect(rule.run({ age: 30 }).pass).toBe(true); expect(rule.run({}).pass).toBe(true); }); it('should work with complex nested compositions', () => { const userRule = enforce.shape({ id: enforce.isNumber(), profile: enforce.shape({ name: enforce.isString(), email: enforce.optional(enforce.isString()), }), tags: enforce.optional(enforce.isArrayOf(enforce.isString())), }); type User = typeof userRule.infer; const validUser: User = { id: 1, profile: { name: 'John', email: 'john@example.com', }, tags: ['developer', 'admin'], }; void validUser; expect( userRule.run({ id: 1, profile: { name: 'John', email: 'john@example.com', }, tags: ['developer', 'admin'], }).pass, ).toBe(true); }); });