diff --git a/packages/validator/src/index.ts b/packages/validator/src/index.ts index df45f1e..2ac05cd 100644 --- a/packages/validator/src/index.ts +++ b/packages/validator/src/index.ts @@ -59,10 +59,6 @@ declare module '@typp/core' { [T] extends [string] ? true : false, string | String ] - boolean: [ - [T] extends [boolean] ? true : false, - boolean | Boolean - ] } export interface ValidateTransformEntries { [key: string]: [boolean, any] @@ -70,11 +66,6 @@ declare module '@typp/core' { [T] extends [string] ? true : false, string ] - boolean: [ - [T] extends [boolean] ? true : false, - // TODO resolve literal type - boolean - ] } // TODO https://zod.dev/?id=coercion-for-primitives // export const coerce: typeof tn @@ -362,11 +353,6 @@ export default function validator(t: typeof tn) { validate: input => typeof input === 'string', transform: input => String(input) }) - t.useValidator([Boolean], { - preprocess, - validate: input => typeof input === 'boolean', - transform: input => FALSELY.includes(input) ? false : Boolean(input) - }) t.useValidator([Symbol], { preprocess, validate: input => typeof input === 'symbol', diff --git a/packages/validator/src/types/index.ts b/packages/validator/src/types/index.ts index 360ccc0..e680582 100644 --- a/packages/validator/src/types/index.ts +++ b/packages/validator/src/types/index.ts @@ -1,9 +1,11 @@ import type { t as tn } from '@typp/core' import { bigintValidator } from './primitive.bigint' +import { booleanValidator } from './primitive.boolean' import { numberValidator } from './primitive.number' export function typesValidator(t: typeof tn) { - t.use(numberValidator) t.use(bigintValidator) + t.use(booleanValidator) + t.use(numberValidator) } diff --git a/packages/validator/src/types/primitive.boolean.ts b/packages/validator/src/types/primitive.boolean.ts index e69de29..7d809e5 100644 --- a/packages/validator/src/types/primitive.boolean.ts +++ b/packages/validator/src/types/primitive.boolean.ts @@ -0,0 +1,51 @@ +import type { IsEqual, Switch, t as tn } from '@typp/core' + +import { FALSELY } from '../base' +import { preprocess } from '../utils.inner' + +declare module '@typp/core' { + namespace t { + export interface ValidateExtendsEntries { + boolean: [ + [T] extends [boolean] ? true : false, + boolean | Boolean, + ] + } + export interface ValidateTransformEntries { + boolean: [ + [T] extends [boolean] ? true : false, + Switch<{ + any: [IsEqual, unknown] + bigint: [ + [Input] extends [bigint] ? true : false, + boolean + ] + self: [ + [Input] extends [boolean] ? true : false, + boolean + ] + number: [ + [Input] extends [number] ? true : false, + boolean + ] + string: [ + [Input] extends [string] ? true : false, + boolean, + ] + nullOrUndefined: [ + [Input] extends [null | undefined] ? true : false, + false + ] + }> + ] + } + } +} + +export function booleanValidator(t: typeof tn) { + t.useValidator([Boolean], { + preprocess, + validate: input => typeof input === 'boolean', + transform: input => FALSELY.includes(input) ? false : Boolean(input) + }) +} diff --git a/packages/validator/tests/types/primitives.spec.ts b/packages/validator/tests/types/primitives.spec.ts index f9223c7..c4f4276 100644 --- a/packages/validator/tests/types/primitives.spec.ts +++ b/packages/validator/tests/types/primitives.spec.ts @@ -4,6 +4,7 @@ import { beforeAll, describe, expect, expectTypeOf, test } from 'vitest' import { validatorSkeleton } from '../../src' import { ParseError, ValidateError } from '../../src/base.inner' import { bigintValidator } from '../../src/types/primitive.bigint' +import { booleanValidator } from '../../src/types/primitive.boolean' import { numberValidator } from '../../src/types/primitive.number' beforeAll(() => t.use(validatorSkeleton)) @@ -108,6 +109,153 @@ describe('bigint', () => { }) }) describe('boolean', () => { + beforeAll(() => t.use(booleanValidator)) + test('base', () => { + const r0 = t.boolean().validate(true) + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = t.boolean().validate(false) + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + }) + test('narrow', () => { + const r0 = t.boolean().validate.narrow(true) + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = t.boolean().validate.narrow(false) + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + }) + test('instanceof', () => { + const r0 = t.boolean().validate(Boolean(true)) + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = t.boolean().validate(Object(true)) + expect(r1).toBe(true) + expectTypeOf(r1).toEqualTypeOf() + + const r2 = t.boolean().validate(new class extends Boolean { + constructor() { + super(true) + } + }()) + expect(r2).toBe(true) + expectTypeOf(r2).toEqualTypeOf() + const r3 = t.boolean().validate(new class extends Boolean { + constructor() { super(true) } + valueOf() { return false } + }) + expect(r3).toBe(false) + expectTypeOf(r3).toEqualTypeOf() + const r4 = t.boolean().validate(new class extends Boolean { + constructor() { super(true) } + [Symbol.toPrimitive]() { return false } + }) + expect(r4).toBe(false) + expectTypeOf(r4).toEqualTypeOf() + const r5 = t.boolean().validate(new class extends Boolean { + constructor() { super(true) } + valueOf() { return false } + [Symbol.toPrimitive]() { return true } + }) + expect(r5).toBe(true) + expectTypeOf(r5).toEqualTypeOf() + }) + test('unexpected', () => { + const skm = t.boolean() + expect(() => { + // @ts-expect-error + skm.validate('abc') + }).toThrow(new ValidateError('unexpected', skm, '1')) + }) + describe('parse', () => { + test('transform - primitive.bigint', () => { + const skm = t.boolean() + const r0 = skm.parse(1n) + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = skm.parse(0n) + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + + // with const + const r2 = skm.parse.narrow(1n) + expect(r2).toBe(true) + expectTypeOf(r2).toEqualTypeOf() + const r3 = skm.parse.narrow(0n) + expect(r3).toBe(false) + expectTypeOf(r3).toEqualTypeOf() + }) + test('transform - primitive.number', () => { + const skm = t.boolean() + const r0 = skm.parse(1) + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = skm.parse(0) + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + const r2 = skm.parse(Infinity) + expect(r2).toBe(true) + expectTypeOf(r2).toEqualTypeOf() + const r3 = skm.parse(-Infinity) + expect(r3).toBe(true) + expectTypeOf(r3).toEqualTypeOf() + const r4 = skm.parse(NaN) + expect(r4).toBe(false) + expectTypeOf(r4).toEqualTypeOf() + + // TODO: with const + // const r5 = skm.parse.narrow(1) + // expect(r5).toBe(true) + // expectTypeOf(r5).toEqualTypeOf() + // const r6 = skm.parse.narrow(0) + // expect(r6).toBe(false) + // expectTypeOf(r6).toEqualTypeOf() + // const r7 = skm.parse.narrow(Infinity) + // expect(r7).toBe(true) + // expectTypeOf(r7).toEqualTypeOf() + // const r8 = skm.parse.narrow(-Infinity) + // expect(r8).toBe(false) + // expectTypeOf(r8).toEqualTypeOf() + // const r9 = skm.parse.narrow(NaN) + // expect(r9).toBe(false) + // expectTypeOf(r9).toEqualTypeOf() + }) + test('transform - primitive.string', () => { + const skm = t.boolean() + const r0 = skm.parse('true') + expect(r0).toBe(true) + expectTypeOf(r0).toEqualTypeOf() + const r1 = skm.parse('false') + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + const r2 = skm.parse('abc') + expect(r2).toBe(true) + expectTypeOf(r2).toEqualTypeOf() + const r3 = skm.parse('') + expect(r3).toBe(false) + expectTypeOf(r3).toEqualTypeOf() + + // TODO: with const + }) + test('transform - primitive.symbol', () => {}) + test('transform - literal', () => { + const skm = t.boolean() + const r0 = skm.parse(null) + expect(r0).toBe(false) + expectTypeOf(r0).toEqualTypeOf() + const r1 = skm.parse.narrow(null) + expect(r1).toBe(false) + expectTypeOf(r1).toEqualTypeOf() + const r2 = skm.parse(undefined) + expect(r2).toBe(false) + expectTypeOf(r2).toEqualTypeOf() + const r3 = skm.parse.narrow(undefined) + expect(r3).toBe(false) + expectTypeOf(r3).toEqualTypeOf() + }) + test('transform - constructor.date', () => {}) + }) }) describe('number', () => { beforeAll(() => t.use(numberValidator))