Skip to content

Commit 3d3e535

Browse files
committed
feat: discriminator
1 parent f7f3af8 commit 3d3e535

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

src/api/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { VBase } from './base'
55
import { VBoolean } from './booleans'
66
import { VCore } from './core'
77
import { VFile } from './files'
8-
import { VAnd, VOr } from './junctions'
8+
import { VAnd, VDiscriminator, VOr } from './junctions'
99
import { VNumber } from './numbers'
1010
import { VObject } from './objects'
1111
import { VMap, VRecord } from './records'
@@ -21,6 +21,7 @@ export const v = {
2121
or: VBase.createType(VOr),
2222
and: VBase.createType(VAnd),
2323
string: VBase.createType(VString),
24+
discriminate: VBase.createType(VDiscriminator),
2425
number: VBase.createType(VNumber),
2526
boolean: VBase.createType(VBoolean),
2627
time: VBase.createType(VTime),

src/api/junctions.ts

+12
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,16 @@ export class VAnd<I> extends VCore<I> {
3131
return isValid(v)
3232
}))
3333
}
34+
}
35+
36+
export class VDiscriminator<D extends Record<string, VCore<any>>> extends VCore<ExtractI<D[keyof D]>> {
37+
constructor (discriminator: (val: ExtractI<D[keyof D]>) => string, schemas: D, err = 'doesnt match any of the schema') {
38+
super()
39+
this.addTyping(makeRule<ExtractI<D[keyof D]>>((value) => {
40+
const val = value as ExtractI<D[keyof D]>
41+
const accessor = discriminator(val)
42+
if (!schemas[accessor]) return isInvalid([err], val)
43+
return schemas[accessor].parse(val)
44+
}))
45+
}
3446
}

tests/api/junctions.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,25 @@ describe('and', () => {
2323
expect(rules.parse('').valid).toBe(false)
2424
expect(rules.parse(false).valid).toBe(false)
2525
})
26+
})
27+
28+
describe('discriminate', () => {
29+
test('discriminate', () => {
30+
const rules = v.discriminate((v) => v, {
31+
'ha': v.string(),
32+
'make': v.string().has(4),
33+
})
34+
expect(rules.parse('ha').valid).toBe(true)
35+
expect(rules.parse(2).valid).toBe(false)
36+
expect(rules.parse('make').valid).toBe(true)
37+
expect(rules.parse('made').valid).toBe(false)
38+
39+
const objectRules = v.discriminate((v) => v.status, {
40+
'ha': v.object({ status: v.is('ha'), total: v.number().gt(10) }),
41+
'name': v.object({ status: v.is('name'), flow: v.boolean() })
42+
})
43+
expect(objectRules.parse({ 'status': 'ha', total: 12 }).valid).toBe(true)
44+
expect(objectRules.parse({ 'status': 'ha', total: 9 }).valid).toBe(false)
45+
expect(objectRules.parse({ 'status': 'none' }).valid).toBe(false)
46+
})
2647
})

0 commit comments

Comments
 (0)