Skip to content

Commit c90949b

Browse files
committed
fix #397
1 parent c3d53fd commit c90949b

File tree

4 files changed

+65
-21
lines changed

4 files changed

+65
-21
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
**Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
1515
high state of flux, you're at risk of it changing without notice.
1616

17+
# 2.0.2
18+
19+
- **Bug Fix**
20+
- fix #397 (@gcanti)
21+
1722
# 2.0.1
1823

1924
- **Bug Fix**

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "io-ts",
3-
"version": "2.0.1",
3+
"version": "2.0.2",
44
"description": "TypeScript compatible runtime type system for IO validation",
55
"files": [
66
"lib",

src/index.ts

+19-12
Original file line numberDiff line numberDiff line change
@@ -1191,19 +1191,26 @@ export interface IntersectionC<CS extends [Mixed, Mixed, ...Array<Mixed>]>
11911191
> {}
11921192

11931193
const mergeAll = (base: any, us: Array<any>): any => {
1194-
let r: any = base
1195-
for (let i = 0; i < us.length; i++) {
1196-
const u = us[i]
1194+
let equal = true
1195+
let primitive = true
1196+
for (const u of us) {
11971197
if (u !== base) {
1198-
// `u` contains a prismatic value or is the result of a stripping combinator
1199-
if (r === base) {
1200-
r = Object.assign({}, u)
1201-
continue
1202-
}
1203-
for (const k in u) {
1204-
if (u[k] !== base[k] || !r.hasOwnProperty(k)) {
1205-
r[k] = u[k]
1206-
}
1198+
equal = false
1199+
}
1200+
if (isObject(u)) {
1201+
primitive = false
1202+
}
1203+
}
1204+
if (equal) {
1205+
return base
1206+
} else if (primitive) {
1207+
return us[us.length - 1]
1208+
}
1209+
let r: any = {}
1210+
for (const u of us) {
1211+
for (const k in u) {
1212+
if (u[k] !== base[k] || !r.hasOwnProperty(k)) {
1213+
r[k] = u[k]
12071214
}
12081215
}
12091216
}

test/intersection.ts

+40-8
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,32 @@ describe('intersection', () => {
8787
assertStrictEqual(T.decode(value), value)
8888
})
8989

90-
it('should play well with stripping combinators', () => {
91-
const A = t.exact(t.type({ a: t.string }))
92-
const B = t.exact(t.type({ b: t.number }))
93-
const T = t.intersection([A, B])
94-
assertSuccess(T.decode({ a: 'a', b: 1 }))
95-
assertSuccess(T.decode({ a: 'a', b: 1, c: true }), { a: 'a', b: 1 })
96-
assertFailure(T, { a: 'a' }, [
90+
it('should play well with exact', () => {
91+
const T1 = t.intersection([t.exact(t.type({ a: t.string })), t.exact(t.type({ b: t.number }))])
92+
assertSuccess(T1.decode({ a: 'a', b: 1 }), { a: 'a', b: 1 })
93+
assertSuccess(T1.decode({ a: 'a', b: 1, c: true }), { a: 'a', b: 1 })
94+
assertFailure(T1, { a: 'a' }, [
9795
'Invalid value undefined supplied to : ({| a: string |} & {| b: number |})/1: {| b: number |}/b: number'
9896
])
97+
98+
const T2 = t.intersection([t.exact(t.type({})), t.partial({ a: t.number })])
99+
assertSuccess(T2.decode({}), {})
100+
assertSuccess(T2.decode({ a: 1 }), { a: 1 })
101+
assertSuccess(T2.decode({ a: undefined }), { a: undefined })
102+
assertSuccess(T2.decode({ a: 1, b: true }), { a: 1, b: true } as any)
103+
104+
// #397
105+
const T3 = t.intersection([t.exact(t.type({})), t.exact(t.partial({ a: t.number }))])
106+
assertSuccess(T3.decode({}), {})
107+
assertSuccess(T3.decode({ a: 1 }), { a: 1 })
108+
assertSuccess(T3.decode({ a: undefined }), { a: undefined })
109+
assertSuccess(T3.decode({ a: 1, b: true }), { a: 1 })
110+
111+
const T4 = t.intersection([t.type({ b: t.string }), t.exact(t.partial({ a: t.number }))])
112+
assertSuccess(T4.decode({ b: 'b' }), { b: 'b' })
113+
assertSuccess(T4.decode({ b: 'b', a: 1 }), { b: 'b', a: 1 })
114+
assertSuccess(T4.decode({ b: 'b', a: undefined }), { b: 'b', a: undefined })
115+
assertSuccess(T4.decode({ b: 'b', a: 1, c: 2 }), { b: 'b', a: 1, c: 2 } as any)
99116
})
100117
})
101118

@@ -149,8 +166,23 @@ describe('intersection', () => {
149166
const T = t.intersection([t.string] as any)
150167
assert.strictEqual(T.is('a'), true)
151168
assert.strictEqual(T.is(1), false)
152-
assertSuccess(T.decode('a'))
169+
assertSuccess(T.decode('a'), 'a')
153170
assertFailure(T, 1, ['Invalid value 1 supplied to : (string)/0: string'])
154171
assert.strictEqual(T.encode('a'), 'a')
155172
})
173+
174+
it('should handle primitives', () => {
175+
const T1 = t.intersection([t.string, t.string])
176+
assert.strictEqual(T1.is('a'), true)
177+
assert.strictEqual(T1.is(1), false)
178+
assertSuccess(T1.decode('a'), 'a')
179+
assertFailure(T1, 1, [
180+
'Invalid value 1 supplied to : (string & string)/0: string',
181+
'Invalid value 1 supplied to : (string & string)/1: string'
182+
])
183+
assert.strictEqual(T1.encode('a'), 'a')
184+
const T2 = t.intersection([NumberFromString, NumberFromString])
185+
assertSuccess(T2.decode('1'), 1)
186+
assert.strictEqual(T2.encode(1), '1')
187+
})
156188
})

0 commit comments

Comments
 (0)