Skip to content

Commit

Permalink
fix #397
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti committed Dec 16, 2019
1 parent c3d53fd commit c90949b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,11 @@
**Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
high state of flux, you're at risk of it changing without notice.

# 2.0.2

- **Bug Fix**
- fix #397 (@gcanti)

# 2.0.1

- **Bug Fix**
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "io-ts",
"version": "2.0.1",
"version": "2.0.2",
"description": "TypeScript compatible runtime type system for IO validation",
"files": [
"lib",
Expand Down
31 changes: 19 additions & 12 deletions src/index.ts
Expand Up @@ -1191,19 +1191,26 @@ export interface IntersectionC<CS extends [Mixed, Mixed, ...Array<Mixed>]>
> {}

const mergeAll = (base: any, us: Array<any>): any => {
let r: any = base
for (let i = 0; i < us.length; i++) {
const u = us[i]
let equal = true
let primitive = true
for (const u of us) {
if (u !== base) {
// `u` contains a prismatic value or is the result of a stripping combinator
if (r === base) {
r = Object.assign({}, u)
continue
}
for (const k in u) {
if (u[k] !== base[k] || !r.hasOwnProperty(k)) {
r[k] = u[k]
}
equal = false
}
if (isObject(u)) {
primitive = false
}
}
if (equal) {
return base
} else if (primitive) {
return us[us.length - 1]
}
let r: any = {}
for (const u of us) {
for (const k in u) {
if (u[k] !== base[k] || !r.hasOwnProperty(k)) {
r[k] = u[k]
}
}
}
Expand Down
48 changes: 40 additions & 8 deletions test/intersection.ts
Expand Up @@ -87,15 +87,32 @@ describe('intersection', () => {
assertStrictEqual(T.decode(value), value)
})

it('should play well with stripping combinators', () => {
const A = t.exact(t.type({ a: t.string }))
const B = t.exact(t.type({ b: t.number }))
const T = t.intersection([A, B])
assertSuccess(T.decode({ a: 'a', b: 1 }))
assertSuccess(T.decode({ a: 'a', b: 1, c: true }), { a: 'a', b: 1 })
assertFailure(T, { a: 'a' }, [
it('should play well with exact', () => {
const T1 = t.intersection([t.exact(t.type({ a: t.string })), t.exact(t.type({ b: t.number }))])
assertSuccess(T1.decode({ a: 'a', b: 1 }), { a: 'a', b: 1 })
assertSuccess(T1.decode({ a: 'a', b: 1, c: true }), { a: 'a', b: 1 })
assertFailure(T1, { a: 'a' }, [
'Invalid value undefined supplied to : ({| a: string |} & {| b: number |})/1: {| b: number |}/b: number'
])

const T2 = t.intersection([t.exact(t.type({})), t.partial({ a: t.number })])
assertSuccess(T2.decode({}), {})
assertSuccess(T2.decode({ a: 1 }), { a: 1 })
assertSuccess(T2.decode({ a: undefined }), { a: undefined })
assertSuccess(T2.decode({ a: 1, b: true }), { a: 1, b: true } as any)

// #397
const T3 = t.intersection([t.exact(t.type({})), t.exact(t.partial({ a: t.number }))])
assertSuccess(T3.decode({}), {})
assertSuccess(T3.decode({ a: 1 }), { a: 1 })
assertSuccess(T3.decode({ a: undefined }), { a: undefined })
assertSuccess(T3.decode({ a: 1, b: true }), { a: 1 })

const T4 = t.intersection([t.type({ b: t.string }), t.exact(t.partial({ a: t.number }))])
assertSuccess(T4.decode({ b: 'b' }), { b: 'b' })
assertSuccess(T4.decode({ b: 'b', a: 1 }), { b: 'b', a: 1 })
assertSuccess(T4.decode({ b: 'b', a: undefined }), { b: 'b', a: undefined })
assertSuccess(T4.decode({ b: 'b', a: 1, c: 2 }), { b: 'b', a: 1, c: 2 } as any)
})
})

Expand Down Expand Up @@ -149,8 +166,23 @@ describe('intersection', () => {
const T = t.intersection([t.string] as any)
assert.strictEqual(T.is('a'), true)
assert.strictEqual(T.is(1), false)
assertSuccess(T.decode('a'))
assertSuccess(T.decode('a'), 'a')
assertFailure(T, 1, ['Invalid value 1 supplied to : (string)/0: string'])
assert.strictEqual(T.encode('a'), 'a')
})

it('should handle primitives', () => {
const T1 = t.intersection([t.string, t.string])
assert.strictEqual(T1.is('a'), true)
assert.strictEqual(T1.is(1), false)
assertSuccess(T1.decode('a'), 'a')
assertFailure(T1, 1, [
'Invalid value 1 supplied to : (string & string)/0: string',
'Invalid value 1 supplied to : (string & string)/1: string'
])
assert.strictEqual(T1.encode('a'), 'a')
const T2 = t.intersection([NumberFromString, NumberFromString])
assertSuccess(T2.decode('1'), 1)
assert.strictEqual(T2.encode(1), '1')
})
})

0 comments on commit c90949b

Please sign in to comment.