diff --git a/src/index.ts b/src/index.ts
index 0af62149d..99c06b7a6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -861,8 +861,7 @@ export const type =
(props: P, name: string = getInterfaceTypeN
if (UnknownRecord.is(u)) {
for (let i = 0; i < len; i++) {
const k = keys[i]
- const uk = u[k]
- if ((uk === undefined && !hasOwnProperty.call(u, k)) || !types[i].is(uk)) {
+ if (!(k in u) || !types[i].is(u[k])) {
return false
}
}
@@ -878,12 +877,15 @@ export const type =
(props: P, name: string = getInterfaceTypeN
const k = keys[i]
const ak = a[k]
const type = types[i]
- const result = type.validate(ak, appendContext(c, k, type, ak))
+ const result =
+ ak === undefined && !(k in a)
+ ? failure(ak, appendContext(c, k, type, ak))
+ : type.validate(ak, appendContext(c, k, type, ak))
if (isLeft(result)) {
pushAll(errors, result.left)
} else {
const vak = result.right
- if (vak !== ak || (vak === undefined && !hasOwnProperty.call(a, k))) {
+ if (vak !== ak || (vak === undefined && !(k in a))) {
/* istanbul ignore next */
if (a === o) {
a = { ...o }
diff --git a/test/exact.ts b/test/exact.ts
index 9d75ae81d..94299c3ce 100644
--- a/test/exact.ts
+++ b/test/exact.ts
@@ -64,7 +64,7 @@ describe('exact', () => {
it('should succeed validating an undefined field', () => {
const T = t.exact(t.type({ foo: t.string, bar: t.union([t.string, t.undefined]) }))
- assertSuccess(T.decode({ foo: 'foo' }))
+ assertSuccess(T.decode({ foo: 'foo', bar: undefined }))
})
it('should return the same reference if validation succeeded', () => {
diff --git a/test/recursion.ts b/test/recursion.ts
index 537c474c4..2dc3e5944 100644
--- a/test/recursion.ts
+++ b/test/recursion.ts
@@ -71,11 +71,19 @@ describe('recursion', () => {
assertStrictEqual(T.decode(value), value)
})
- it('should fail validating an invalid value', () => {
+ it('should fail validating { a: number, b: ( self | undefined | null) } for value 1', () => {
assertFailure(T, 1, ['Invalid value 1 supplied to : T'])
- assertFailure(T, {}, ['Invalid value undefined supplied to : T/a: number'])
+ })
+ it('should fail validating { a: number, b: ( self | undefined | null) } for value {}', () => {
+ assertFailure(T, {}, [
+ 'Invalid value undefined supplied to : T/a: number',
+ 'Invalid value undefined supplied to : T/b: (T | undefined | null)'
+ ])
+ })
+ it('should fail validating { a: number, b: ( self | undefined | null) } for value { a: 1, b: {} }', () => {
assertFailure(T, { a: 1, b: {} }, [
'Invalid value undefined supplied to : T/b: (T | undefined | null)/0: T/a: number',
+ 'Invalid value undefined supplied to : T/b: (T | undefined | null)/0: T/b: (T | undefined | null)',
'Invalid value {} supplied to : T/b: (T | undefined | null)/1: undefined',
'Invalid value {} supplied to : T/b: (T | undefined | null)/2: null'
])
diff --git a/test/strict.ts b/test/strict.ts
index 35a4cf7d4..c0357b3e8 100644
--- a/test/strict.ts
+++ b/test/strict.ts
@@ -30,7 +30,7 @@ describe('strict', () => {
assert.strictEqual(T.is(undefined), false)
})
- it('#423', () => {
+ it('should allow properties to be satisified by getters - #423', () => {
class A {
get a() {
return 'a'
@@ -42,6 +42,26 @@ describe('strict', () => {
const T = t.strict({ a: t.string, b: t.string })
assert.strictEqual(T.is(new A()), true)
})
+
+ it('should return false for a missing undefined property ', () => {
+ const T = t.strict({ a: t.string, b: t.undefined })
+ assert.strictEqual(T.is({ a: 'a' }), false)
+ })
+
+ it('should return false for a missing unknown property ', () => {
+ const T = t.strict({ a: t.string, b: t.unknown })
+ assert.strictEqual(T.is({ a: 'a' }), false)
+ })
+
+ it('should return true for a missing undefined property that is optional', () => {
+ const T = t.intersection([t.type({ a: t.string }), t.partial({ b: t.undefined })])
+ assert.strictEqual(T.is({ a: 'a' }), true)
+ })
+
+ it('should return true for a missing unknown property that is optional', () => {
+ const T = t.intersection([t.type({ a: t.string }), t.partial({ b: t.unknown })])
+ assert.strictEqual(T.is({ a: 'a' }), true)
+ })
})
describe('decode', () => {
@@ -52,7 +72,7 @@ describe('strict', () => {
it('should succeed validating an undefined field', () => {
const T = t.strict({ foo: t.string, bar: t.union([t.string, t.undefined]) })
- assertSuccess(T.decode({ foo: 'foo' }))
+ assertSuccess(T.decode({ foo: 'foo', bar: undefined }))
})
it('should return the same reference if validation succeeded', () => {
@@ -66,6 +86,26 @@ describe('strict', () => {
assertFailure(T, { foo: 1 }, ['Invalid value 1 supplied to : {| foo: string |}/foo: string'])
})
+ it('should fail validating a missing undefined value', () => {
+ const T = t.strict({ a: t.string, b: t.undefined })
+ assertFailure(T, { a: 'a' }, ['Invalid value undefined supplied to : {| a: string, b: undefined |}/b: undefined'])
+ })
+
+ it('should fail validating a missing unknown value', () => {
+ const T = t.strict({ a: t.string, b: t.unknown })
+ assertFailure(T, { a: 'a' }, ['Invalid value undefined supplied to : {| a: string, b: unknown |}/b: unknown'])
+ })
+
+ it('should succeed validating a missing undefined value that is optional', () => {
+ const T = t.intersection([t.type({ a: t.string }), t.partial({ b: t.unknown })])
+ assertSuccess(T.decode({ a: 'a' }))
+ })
+
+ it('should succeed validating a missing unknown value that is optional', () => {
+ const T = t.intersection([t.type({ a: t.string }), t.partial({ b: t.unknown })])
+ assertSuccess(T.decode({ a: 'a' }))
+ })
+
it('should strip additional properties', () => {
const T = t.strict({ foo: t.string })
assertSuccess(T.decode({ foo: 'foo', bar: 1, baz: true }), { foo: 'foo' })
diff --git a/test/type.ts b/test/type.ts
index 813dbb8a6..d6c7fee04 100644
--- a/test/type.ts
+++ b/test/type.ts
@@ -17,20 +17,328 @@ describe('type', () => {
})
})
- describe('is', () => {
- it('should return `true` on valid inputs', () => {
- const T = t.type({ a: t.string })
- assert.strictEqual(T.is({ a: 'a' }), true)
+ const successCases = [
+ // [ props, name, value ]
+ [{ a: t.null }, undefined, { a: null }],
+ [{ a: t.nullType }, undefined, { a: null }],
+ [{ a: t.undefined }, undefined, { a: undefined }],
+ [{ a: t.void }, undefined, { a: undefined }],
+ [{ a: t.voidType }, undefined, { a: undefined }],
+ [{ a: t.unknown }, undefined, { a: 'a' }],
+ [{ a: t.unknown }, undefined, { a: undefined }],
+ [{ a: t.string }, undefined, { a: 'a' }],
+ [{ a: t.number }, undefined, { a: Number.MAX_VALUE }],
+ [{ a: t.bigint }, undefined, { a: BigInt(Number.MAX_VALUE + 1), toJSON: () => `a: BigInt(Number.MAX_VALUE +1)` }],
+ [{ a: t.boolean }, undefined, { a: true }],
+ [{ a: t.UnknownArray }, undefined, { a: [[1], [2]] }],
+ [{ a: t.UnknownArray }, undefined, { a: [] }],
+ [{ a: t.UnknownArray }, undefined, { a: [undefined] }],
+ [{ a: t.UnknownArray }, undefined, { a: [[undefined], [2]] }],
+ [{ a: t.array(t.number) }, 'Array', { a: [1, 2] }],
+ [{ a: t.UnknownRecord }, undefined, { a: { b: 'b' } }],
+ [{ a: t.UnknownRecord }, undefined, { a: {} }],
+ [{ a: t.UnknownRecord }, undefined, { a: { undefined } }],
+ [{ a: t.UnknownRecord }, undefined, { a: { b: undefined } }],
+ [{ a: t.record(t.string, t.string) }, '{ a: { [K in string]: string } }', { a: { b: 'b' } }],
+ [{ a: t.Int }, '{ a: Int }', { a: -1 }],
+ [{ a: t.literal('YES') }, '{ a: "YES" }', { a: 'YES' }],
+ [{ a: t.partial({ b: t.string, c: t.string }) }, '{ a: Partial<{ b: string, c:string }> }', { a: {} }],
+ [
+ { a: t.partial({ b: t.string, c: t.string }) },
+ '{ a: Partial<{ b: string, c: string }> }',
+ { a: { b: 'b', c: undefined } }
+ ],
+ [{ a: t.readonly(t.number) }, '{ a: Readonly }', { a: 1 }],
+ [{ a: t.readonlyArray(t.number) }, '{ a: ReadonlyArray }', { a: [1, 2] }],
+ [{ a: t.type({ b: t.string, c: t.string }) }, '{ a: { b: string, c: string }', { a: { b: 'b', c: 'c' } }],
+ [{ a: t.tuple([t.string, t.string]) }, '{ a: [string, string] }', { a: ['A', 'B'] }],
+ [
+ { a: t.union([t.string, t.number]), b: t.union([t.string, t.number]) },
+ '{ a: (string | number), b: (string | number) }',
+ { a: 1, b: 'b' }
+ ],
+ [{ a: t.intersection([t.number, t.Int]) }, '{ a: (number & Int) }', { a: 1 }],
+ [
+ {
+ a: t.brand(
+ t.number,
+ (n: any): n is t.Branded => n >= 0,
+ 'Positive'
+ )
+ },
+ '{ a: Positive }',
+ { a: 1 }
+ ],
+ [{ a: t.keyof({ foo: null, bar: null }) }, '{ a: "foo" | "bar" }', { a: 'foo' }],
+ [
+ { a: t.exact(t.type({ x: t.number, y: t.number })) },
+ '{ a: {| x: number, y: number |} }',
+ { a: { x: 1, y: 2, z: 3 } }
+ ],
+ [{ a: t.strict({ x: t.number, y: t.number }) }, '{ a: {| x: number, y: number |} }', { a: { x: 1, y: 2, z: 3 } }]
+ ]
+
+ describe('`is` should return `true` for', () => {
+ test.each(successCases)('props: %p, name: %p given valid input %j', (props, name, value) => {
+ const T = t.type(props, name)
+ assert.strictEqual(T.is(value), true)
})
+ })
- it('should return `false` on invalid inputs', () => {
- const T = t.type({ a: t.string })
- assert.strictEqual(T.is({}), false)
- assert.strictEqual(T.is({ a: 1 }), false)
- // #407
- assert.strictEqual(T.is([]), false)
+ describe('`decode` should succeed decoding with', () => {
+ test.each(successCases)('props: %p, name: %p, value: %j', (props, name, value) => {
+ const T = t.type(props, name)
+ assertSuccess(T.decode(value))
+ })
+ })
+
+ const failureCases = [
+ // [ props, name, value, messages ]
+ [{ a: t.null }, '{ a: null }', { a: 'a' }, ['Invalid value "a" supplied to : { a: null }/a: null']],
+ [{ a: t.null }, '{ a: null }', {}, ['Invalid value undefined supplied to : { a: null }/a: null']],
+ [{ a: t.nullType }, '{ a: null }', { a: undefined }, ['Invalid value undefined supplied to : { a: null }/a: null']],
+ [
+ { a: t.undefined },
+ '{ a: undefined }',
+ { a: 'a' },
+ ['Invalid value "a" supplied to : { a: undefined }/a: undefined']
+ ],
+ [
+ { a: t.undefined },
+ '{ a: undefined }',
+ {},
+ ['Invalid value undefined supplied to : { a: undefined }/a: undefined']
+ ],
+ [{ a: t.void }, '{ a: void }', { a: null }, ['Invalid value null supplied to : { a: void }/a: void']],
+ [{ a: t.unknown }, '{ a: unknown }', {}, ['Invalid value undefined supplied to : { a: unknown }/a: unknown']],
+ [{ a: t.string }, '{ a: string }', 1, ['Invalid value 1 supplied to : { a: string }']],
+ [{ a: t.string }, '{ a: string }', {}, ['Invalid value undefined supplied to : { a: string }/a: string']],
+ [
+ { a: t.string },
+ '{ a: string }',
+ { a: undefined },
+ ['Invalid value undefined supplied to : { a: string }/a: string']
+ ],
+ [{ a: t.string }, '{ a: string }', { a: 1 }, ['Invalid value 1 supplied to : { a: string }/a: string']],
+ [{ a: t.string }, '{ a: string }', [], ['Invalid value [] supplied to : { a: string }']], // #407
+ [{ a: t.number }, '{ a: number }', { a: 'a' }, ['Invalid value "a" supplied to : { a: number }/a: number']],
+ [{ a: t.number }, '{ a: number }', {}, ['Invalid value undefined supplied to : { a: number }/a: number']],
+ [{ a: t.bigint }, '{ a: bigint }', { a: 'a' }, ['Invalid value "a" supplied to : { a: bigint }/a: bigint']],
+ [{ a: t.bigint }, '{ a: bigint }', {}, ['Invalid value undefined supplied to : { a: bigint }/a: bigint']],
+ [{ a: t.boolean }, '{ a: boolean }', { a: 1 }, ['Invalid value 1 supplied to : { a: boolean }/a: boolean']],
+ [{ a: t.boolean }, '{ a: boolean }', {}, ['Invalid value undefined supplied to : { a: boolean }/a: boolean']],
+ [
+ { a: t.UnknownArray },
+ '{ a: UnknownArray }',
+ { a: 'a' },
+ ['Invalid value "a" supplied to : { a: UnknownArray }/a: UnknownArray']
+ ],
+ [
+ { a: t.UnknownArray },
+ '{ a: UnknownArray }',
+ {},
+ ['Invalid value undefined supplied to : { a: UnknownArray }/a: UnknownArray']
+ ],
+ [
+ { a: t.UnknownArray },
+ '{ a: UnknownArray }',
+ { a: undefined },
+ ['Invalid value undefined supplied to : { a: UnknownArray }/a: UnknownArray']
+ ],
+ [
+ { a: t.array(t.number) },
+ '{ a: Array }',
+ { a: 1 },
+ ['Invalid value 1 supplied to : { a: Array }/a: Array']
+ ],
+ [
+ { a: t.array(t.number) },
+ '{ a: Array }',
+ {},
+ ['Invalid value undefined supplied to : { a: Array }/a: Array']
+ ],
+ [
+ { a: t.UnknownRecord },
+ '{ a: UnknownRecord }',
+ { a: [1] },
+ ['Invalid value [1] supplied to : { a: UnknownRecord }/a: UnknownRecord']
+ ],
+ [
+ { a: t.UnknownRecord },
+ '{ a: UnknownRecord }',
+ {},
+ ['Invalid value undefined supplied to : { a: UnknownRecord }/a: UnknownRecord']
+ ],
+ [
+ { a: t.UnknownRecord },
+ '{ a: UnknownRecord }',
+ { a: undefined },
+ ['Invalid value undefined supplied to : { a: UnknownRecord }/a: UnknownRecord']
+ ],
+ [
+ { a: t.record(t.string, t.string) },
+ '{ a: { [K in string]: string } }',
+ { a: 1 },
+ ['Invalid value 1 supplied to : { a: { [K in string]: string } }/a: { [K in string]: string }']
+ ],
+ [
+ { a: t.record(t.string, t.string) },
+ '{ a: { [K in string]: string } }',
+ {},
+ ['Invalid value undefined supplied to : { a: { [K in string]: string } }/a: { [K in string]: string }']
+ ],
+ [{ a: t.Int }, '{ a: Int }', { a: -1.1 }, ['Invalid value -1.1 supplied to : { a: Int }/a: Int']],
+ [{ a: t.Int }, '{ a: Int }', {}, ['Invalid value undefined supplied to : { a: Int }/a: Int']],
+ [{ a: t.literal('YES') }, '{ a: "YES" }', { a: 'NO' }, ['Invalid value "NO" supplied to : { a: "YES" }/a: "YES"']],
+ [{ a: t.literal('YES') }, '{ a: "YES" }', {}, ['Invalid value undefined supplied to : { a: "YES" }/a: "YES"']],
+ [
+ { a: t.partial({ b: t.string, c: t.string }) },
+ '{ a: Partial<{ b: string, c: string }> }',
+ { a: { b: 1 } },
+ [
+ 'Invalid value 1 supplied to : ' +
+ '{ a: Partial<{ b: string, c: string }> }/a: Partial<{ b: string, c: string }>/b: string'
+ ]
+ ],
+ [
+ { a: t.partial({ b: t.string, c: t.string }) },
+ '{ a: Partial<{ b: string, c: string }> }',
+ {},
+ [
+ 'Invalid value undefined supplied to : ' +
+ '{ a: Partial<{ b: string, c: string }> }/a: Partial<{ b: string, c: string }>'
+ ]
+ ],
+ [
+ { a: t.readonly(t.number) },
+ '{ a: Readonly }',
+ { a: 'a' },
+ ['Invalid value "a" supplied to : { a: Readonly }/a: Readonly']
+ ],
+ [
+ { a: t.readonly(t.number) },
+ '{ a: Readonly }',
+ {},
+ ['Invalid value undefined supplied to : { a: Readonly }/a: Readonly']
+ ],
+ [
+ { a: t.readonlyArray(t.number) },
+ '{ a: ReadonlyArray }',
+ { a: 1 },
+ ['Invalid value 1 supplied to : { a: ReadonlyArray }/a: ReadonlyArray']
+ ],
+ [
+ { a: t.readonlyArray(t.number) },
+ '{ a: ReadonlyArray }',
+ {},
+ ['Invalid value undefined supplied to : { a: ReadonlyArray }/a: ReadonlyArray']
+ ],
+ [
+ { a: t.tuple([t.string, t.string]) },
+ '{ a: [string, string] }',
+ { a: [1, 2] },
+ [
+ 'Invalid value 1 supplied to : { a: [string, string] }/a: [string, string]/0: string',
+ 'Invalid value 2 supplied to : { a: [string, string] }/a: [string, string]/1: string'
+ ]
+ ],
+ [
+ { a: t.tuple([t.string, t.string]) },
+ '{ a: [string, string] }',
+ {},
+ ['Invalid value undefined supplied to : { a: [string, string] }/a: [string, string]']
+ ],
+ [
+ { a: t.union([t.string, t.number]), b: t.union([t.string, t.number]) },
+ '{ a: (string | number), b: (string | number) }',
+ { a: [1], b: ['b'] },
+ [
+ 'Invalid value [1] supplied to : { a: (string | number), b: (string | number) }/a: (string | number)/0: string',
+ 'Invalid value [1] supplied to : { a: (string | number), b: (string | number) }/a: (string | number)/1: number',
+ 'Invalid value ["b"] supplied to : { a: (string | number), b: (string | number) }/b: (string | number)/0: string',
+ 'Invalid value ["b"] supplied to : { a: (string | number), b: (string | number) }/b: (string | number)/1: number'
+ ]
+ ],
+ [
+ { a: t.union([t.string, t.number]), b: t.union([t.string, t.number]) },
+ '{ a: (string | number), b: (string | number) }',
+ {},
+ [
+ 'Invalid value undefined supplied to : { a: (string | number), b: (string | number) }/a: (string | number)',
+ 'Invalid value undefined supplied to : { a: (string | number), b: (string | number) }/b: (string | number)'
+ ]
+ ],
+ [
+ { a: t.intersection([t.number, t.Int]) },
+ '{ a: (number & Int) }',
+ { a: 'a' },
+ [
+ 'Invalid value "a" supplied to : { a: (number & Int) }/a: (number & Int)/0: number',
+ 'Invalid value "a" supplied to : { a: (number & Int) }/a: (number & Int)/1: Int'
+ ]
+ ],
+ [
+ { a: t.intersection([t.number, t.Int]) },
+ '{ a: (number & Int) }',
+ {},
+ ['Invalid value undefined supplied to : { a: (number & Int) }/a: (number & Int)']
+ ],
+ [
+ { a: t.brand(t.number, (n): n is t.Branded => n >= 0, 'Positive') },
+ '{ a: Positive }',
+ { a: 'a' },
+ ['Invalid value "a" supplied to : { a: Positive }/a: Positive']
+ ],
+ [
+ { a: t.brand(t.number, (n): n is t.Branded => n >= 0, 'Positive') },
+ '{ a: Positive }',
+ {},
+ ['Invalid value undefined supplied to : { a: Positive }/a: Positive']
+ ],
+ [
+ { a: t.keyof({ foo: null, bar: null }) },
+ '{ a: "foo" | "bar" }',
+ { a: 'baz' },
+ ['Invalid value "baz" supplied to : { a: "foo" | "bar" }/a: "foo" | "bar"']
+ ],
+ [
+ { a: t.keyof({ foo: null, bar: null }) },
+ '{ a: "foo" | "bar" }',
+ {},
+ ['Invalid value undefined supplied to : { a: "foo" | "bar" }/a: "foo" | "bar"']
+ ],
+ [
+ { a: t.exact(t.type({ x: t.number, y: t.number })) },
+ '{ a: {| x: number, y: number |} }',
+ { a: { x: 1, z: 3 } },
+ [
+ 'Invalid value undefined supplied to : ' +
+ '{ a: {| x: number, y: number |} }/a: {| x: number, y: number |}/y: number'
+ ]
+ ],
+ [
+ { a: t.strict({ x: t.number, y: t.number }) },
+ '{ a: {| x: number, y: number |} }',
+ {},
+ ['Invalid value undefined supplied to : ' + '{ a: {| x: number, y: number |} }/a: {| x: number, y: number |}']
+ ]
+ ]
+
+ describe('`is` should return `false` for', () => {
+ test.each(failureCases)('props: %p, name: %p, value: %j', (props, name, value) => {
+ const T = t.type(props, name)
+ assert.strictEqual(T.is(value), false)
+ })
+ })
+
+ describe('`decode` should fail decoding with', () => {
+ test.each(failureCases)('props: %p, name: %p, value: %j', (props, name, value, messages) => {
+ const T = t.type(props, name)
+ assertFailure(T, value, messages)
})
+ })
+ describe('is', () => {
// #434
it('should return `false` on missing fields', () => {
const T = t.type({ a: t.unknown })
@@ -42,6 +350,26 @@ describe('type', () => {
assert.strictEqual(T.is({ a: 'a', b: 1 }), true)
})
+ it('should handle recursive types properly', () => {
+ interface Tree {
+ name: string
+ children?: Array | undefined
+ }
+
+ const Tree: t.Type = t.recursion('Tree', () =>
+ t.intersection([t.type({ name: t.string }), t.partial({ children: t.union([t.array(Tree), t.undefined]) })])
+ )
+
+ const subtree: Tree = { name: 'subtree', children: [] }
+ const childlessSubtree: Tree = { name: 'childless' }
+ const invalidSubtree = { name: 'invalid', children: 'children ' }
+
+ assert.strictEqual(Tree.is({ name: 'a', children: [subtree] }), true)
+ assert.strictEqual(Tree.is({ name: 'b', children: [childlessSubtree] }), true)
+ assert.strictEqual(Tree.is({ name: 'c' }), true)
+ assert.strictEqual(Tree.is({ name: 'd', children: [invalidSubtree] }), false)
+ })
+
it('#423', () => {
class A {
get a() {
@@ -67,27 +395,19 @@ describe('type', () => {
assertSuccess(T.decode({ a: '1' }), { a: 1 })
})
- it('should decode undefined properties as always present keys', () => {
+ it('should decode undefined properties', () => {
const T1 = t.type({ a: t.undefined })
assertSuccess(T1.decode({ a: undefined }), { a: undefined })
- assertSuccess(T1.decode({}), { a: undefined })
const T2 = t.type({ a: t.union([t.number, t.undefined]) })
assertSuccess(T2.decode({ a: undefined }), { a: undefined })
assertSuccess(T2.decode({ a: 1 }), { a: 1 })
- assertSuccess(T2.decode({}), { a: undefined })
const T3 = t.type({ a: t.unknown })
- assertSuccess(T3.decode({}), { a: undefined })
- })
+ assertSuccess(T3.decode({ a: undefined }), { a: undefined })
- it('should fail decoding an invalid value', () => {
- const T = t.type({ a: t.string })
- assertFailure(T, 1, ['Invalid value 1 supplied to : { a: string }'])
- assertFailure(T, {}, ['Invalid value undefined supplied to : { a: string }/a: string'])
- assertFailure(T, { a: 1 }, ['Invalid value 1 supplied to : { a: string }/a: string'])
- // #407
- assertFailure(T, [], ['Invalid value [] supplied to : { a: string }'])
+ const T4 = t.type({ a: t.void })
+ assertSuccess(T4.decode({ a: undefined }), { a: undefined })
})
it('should support the alias `interface`', () => {