Skip to content

Commit

Permalink
Allow objects to be part of tuples (fixes #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
pfumagalli committed Dec 6, 2021
1 parent d55e985 commit d80f5a7
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 12 deletions.
17 changes: 13 additions & 4 deletions src/validators/object.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
InferSchema,
Schema,
TupleRestParameter,
ValidationOptions,
Validator,
additionalValidator,
modifierValidator,
never,
restValidator,
schemaValidator,
} from '../types'
import { assertValidation, ValidationErrorBuilder } from '../errors'
Expand Down Expand Up @@ -122,13 +124,20 @@ export class ObjectValidator<S extends Schema> extends Validator<InferSchema<S>>
const anyObjectValidator = new AnyObjectValidator()

function _object(): Validator<Record<string, any>>
function _object<S extends Schema>(schema: S): S
function _object<S extends Schema>(schema: S): S & {
[Symbol.iterator](): Generator<TupleRestParameter<InferSchema<S>>>
}
function _object(schema?: Schema): Validator<Record<string, any>> | Schema {
if (! schema) return anyObjectValidator

return Object.defineProperty(schema, schemaValidator, {
value: new ObjectValidator(schema),
enumerable: false,
const validator = new ObjectValidator(schema)
function* iterator(): Generator<TupleRestParameter> {
yield { [restValidator]: validator }
}

return Object.defineProperties(schema, {
[schemaValidator]: { value: validator, enumerable: false },
[Symbol.iterator]: { value: iterator, enumerable: false },
})
}

Expand Down
27 changes: 25 additions & 2 deletions test-d/05-tuples.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,31 @@ expectType<[ string, number ]>(result[9])

expectType<string>(result[1][0])
expectType<number>(result[1][1])
// ??? why ??? expectError(result[1][2])

expectType<string>(result[9][0])
expectType<number>(result[9][1])
// ??? why ??? expectError(result[9][2])

// objects in tuples
const testObject = object({ foo: string })

const tuple3 = tuple([ string, testObject ] as const)
const result3 = validate(tuple3, null)

expectAssignable<[ string, { foo: string }]>(result3)
expectType<string>(result3[0])

expectAssignable<{ foo: string }>(result3[1])
expectType<string>(result3[1].foo)

// objects in tuples as rest parameters
const tuple4 = tuple([ string, ...testObject ] as const)
const result4 = validate(tuple4, null)

expectAssignable<[ string, ...({ foo: string })[]]>(result4)
expectType<string>(result4[0])

expectAssignable<{ foo: string }>(result4[1])
expectType<string>(result4[1].foo)

expectAssignable<{ foo: string }>(result4[999])
expectType<string>(result4[999].foo)
34 changes: 28 additions & 6 deletions test/15-tuples.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { boolean, number, string, tuple, validate, ValidationError } from '../src/index'
import { boolean, number, object, string, tuple, validate, ValidationError } from '../src/index'
import { expect } from 'chai'

describe('Tuple validator', () => {
Expand Down Expand Up @@ -31,7 +31,8 @@ describe('Tuple validator', () => {
it('should validate a simple tuple', () => {
const validator = tuple([ string, number, boolean ])

expect(validate(validator, [ 'foo', 123, true ])).to.eql([ 'foo', 123, true ])
expect(validate(validator, [ 'foo', 123, true ]))
.to.eql([ 'foo', 123, true ])

expect(() => validate(validator, [ 123 ]))
.to.throw(ValidationError, 'Found 1 validation error')
Expand Down Expand Up @@ -91,9 +92,30 @@ describe('Tuple validator', () => {

it('should validate a tuple with only rest parameters', () => {
const v1 = tuple([ ...string, ...number ])
expect(validate(v1, [ 'foo', 'bar', 123, 456 ])).to.eql([ 'foo', 'bar', 123, 456 ])
expect(validate(v1, [ 'foo', 'bar' ])).to.eql([ 'foo', 'bar' ])
expect(validate(v1, [ 123, 456 ])).to.eql([ 123, 456 ])
expect(validate(v1, [])).to.eql([])
expect(validate(v1, [ 'foo', 'bar', 123, 456 ]))
.to.eql([ 'foo', 'bar', 123, 456 ])

expect(validate(v1, [ 'foo', 'bar' ]))
.to.eql([ 'foo', 'bar' ])

expect(validate(v1, [ 123, 456 ]))
.to.eql([ 123, 456 ])

expect(validate(v1, []))
.to.eql([])
})

it('should validate a tuple with an object rest parameter', () => {
const o1 = object({ foo: string } as const)
const v1 = tuple([ number, ...o1, boolean ])

expect(validate(v1, [ 123, true ]))
.to.eql([ 123, true ])

expect(validate(v1, [ 123, { foo: 'bar' }, true ]))
.to.eql([ 123, { foo: 'bar' }, true ])

expect(validate(v1, [ 123, { foo: 'bar' }, { foo: 'baz' }, true ]))
.to.eql([ 123, { foo: 'bar' }, { foo: 'baz' }, true ])
})
})

0 comments on commit d80f5a7

Please sign in to comment.