Skip to content

Commit 647e530

Browse files
committed
fix getTags algorithm for mutually recursive codecs, closes #354
1 parent a4c6c5f commit 647e530

File tree

4 files changed

+28
-11
lines changed

4 files changed

+28
-11
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
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.1
18+
19+
- **Bug Fix**
20+
- fix `getTags` algorithm for mutually recursive codecs, closes #354 (@gcanti)
21+
1722
# 2.0.0
1823

1924
- **Breaking Change**

package.json

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

src/index.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,10 +1985,6 @@ export function alias<A, O, I>(
19851985
return () => codec as any
19861986
}
19871987

1988-
//
1989-
// backporting
1990-
//
1991-
19921988
interface NonEmptyArray<A> extends Array<A> {
19931989
0: A
19941990
}
@@ -2089,13 +2085,13 @@ function isRecursiveC(codec: Any): codec is RecursiveType<Any> {
20892085
return (codec as any)._tag === 'RecursiveType'
20902086
}
20912087

2092-
let lazyCodec: Any | null = null
2088+
const lazyCodecs: Array<Any> = []
20932089

20942090
/**
20952091
* @internal
20962092
*/
20972093
export function getTags(codec: Any): Tags {
2098-
if (codec === lazyCodec) {
2094+
if (lazyCodecs.indexOf(codec) !== -1) {
20992095
return emptyTags
21002096
}
21012097
if (isTypeC(codec) || isStrictC(codec)) {
@@ -2118,9 +2114,9 @@ export function getTags(codec: Any): Tags {
21182114
} else if (isUnionC(codec)) {
21192115
return codec.types.slice(1).reduce((tags, codec) => intersectTags(tags, getTags(codec)), getTags(codec.types[0]))
21202116
} else if (isRecursiveC(codec)) {
2121-
lazyCodec = codec
2117+
lazyCodecs.push(codec)
21222118
const tags = getTags(codec.type)
2123-
lazyCodec = null
2119+
lazyCodecs.pop()
21242120
return tags
21252121
}
21262122
return emptyTags

test/recursion.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ describe('recursion', () => {
126126
type B = {
127127
a: A | null
128128
}
129-
const A: t.Type<A> = t.recursion('A', self =>
129+
const A: t.Type<A> = t.recursion('A', () =>
130130
t.type({
131-
b: t.union([self, B, t.null])
131+
b: t.union([A, B, t.null])
132132
})
133133
)
134134
const B: t.Type<B> = t.recursion('B', () =>
@@ -138,5 +138,21 @@ describe('recursion', () => {
138138
)
139139
assert.strictEqual(A.is({ b: { b: null } }), true)
140140
assert.strictEqual(A.is({ b: { a: { b: { a: null } } } }), true)
141+
142+
// #354
143+
interface C1A {
144+
a: C1A | string
145+
}
146+
const C1: t.Type<C1A> = t.recursion('C1', () =>
147+
t.type({
148+
a: t.union([C2, t.string])
149+
})
150+
)
151+
const C2: t.Type<C1A> = t.recursion('C2', () => C1)
152+
const C3 = t.union([C1, t.string])
153+
154+
assert.strictEqual(C3.is({ a: 'a' }), true)
155+
assert.strictEqual(C3.is('a'), true)
156+
assert.strictEqual(C3.is({ a: { a: 'a' } }), true)
141157
})
142158
})

0 commit comments

Comments
 (0)