effect-app 4.0.0-beta.23 → 4.0.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +30 -11
- package/package.json +1 -1
- package/src/Schema.ts +30 -8
- package/test/schema.test.ts +147 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @effect-app/prelude
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.24
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 32f71bf: Fix `Schema.TaggedUnion(...).tags` extraction for class-based members (for example `TaggedClass`) by using a local AST sentinel walker instead of relying on internal effect APIs.
|
|
8
|
+
|
|
9
|
+
Add tests covering:
|
|
10
|
+
|
|
11
|
+
- `TaggedUnion` with `encodeKeys`-wrapped members
|
|
12
|
+
- `TaggedUnion` with `TaggedClass` members
|
|
13
|
+
|
|
3
14
|
## 4.0.0-beta.23
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/Schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAA;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE5E,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,KAAK,WAAW,IAAI,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAG1G,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,gBAAgB,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AAE/D,cAAc,mBAAmB,CAAA;AACjC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9G,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,WAAW,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAA;AAEnD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,eAAe,CAAA;AAE7C,eAAO,MAAM,MAAM,eAAW,CAAA;AAC9B,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAA;AAElC,MAAM,WAAW,gBAAgB;IAC/B,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAA;CACvB;AAKD,eAAO,MAAM,KAAK,iGAOf,CAAA;AAEH,MAAM,MAAM,KAAK,GAAG,SAAS,CAAA;AAE7B,eAAO,MAAM,WAAW,mQAQrB,CAAA;AAEH,MAAM,MAAM,WAAW,GAAG,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../src/Schema.ts"],"names":[],"mappings":"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"}
|
package/dist/Schema.js
CHANGED
|
@@ -38,21 +38,40 @@ export const PhoneNumber = PhoneNumberT
|
|
|
38
38
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
39
39
|
fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
|
|
40
40
|
}), withDefaultMake);
|
|
41
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
42
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
41
43
|
const getTagFromAST = (schema) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
for (const ps of ast.propertySignatures) {
|
|
47
|
-
if (ps.name === "_tag" && !SchemaAST.isOptional(ps.type) && SchemaAST.isLiteral(ps.type)) {
|
|
48
|
-
return ps.type.literal;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
44
|
+
const sentinels = collectSentinelsFromAST(schema.ast);
|
|
45
|
+
const sentinel = sentinels.find((s) => s.key === "_tag");
|
|
46
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string")
|
|
47
|
+
return sentinel.literal;
|
|
52
48
|
throw new Error("No _tag literal found on schema member");
|
|
53
49
|
};
|
|
50
|
+
function collectSentinelsFromAST(ast) {
|
|
51
|
+
switch (ast._tag) {
|
|
52
|
+
case "Declaration": {
|
|
53
|
+
const s = ast.annotations?.["~sentinels"];
|
|
54
|
+
return Array.isArray(s) ? s : [];
|
|
55
|
+
}
|
|
56
|
+
case "Objects":
|
|
57
|
+
return ast.propertySignatures.flatMap((ps) => {
|
|
58
|
+
const type = ps.type;
|
|
59
|
+
if (!SchemaAST.isOptional(type)) {
|
|
60
|
+
if (SchemaAST.isLiteral(type))
|
|
61
|
+
return [{ key: ps.name, literal: type.literal }];
|
|
62
|
+
if (SchemaAST.isUniqueSymbol(type))
|
|
63
|
+
return [{ key: ps.name, literal: type.symbol }];
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
});
|
|
67
|
+
case "Suspend":
|
|
68
|
+
return collectSentinelsFromAST(ast.thunk());
|
|
69
|
+
default:
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
54
73
|
export const tags = (self) => S.Literals(self.map(getTagFromAST));
|
|
55
74
|
const extendTaggedUnionWithTags = (schema) => extendM(schema.pipe(S.toTaggedUnion("_tag")), () => ({ tags: tags(schema.members) }));
|
|
56
75
|
export const ExtendTaggedUnion = (schema) => extendTaggedUnionWithTags(schema);
|
|
57
76
|
export const TaggedUnion = (...a) => extendTaggedUnionWithTags(S.Union(a));
|
|
58
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
77
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1NjaGVtYS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFlLE1BQU0sUUFBUSxDQUFBO0FBQy9DLE9BQU8sS0FBSyxDQUFDLE1BQU0sZUFBZSxDQUFBO0FBRWxDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDckMsT0FBTyxFQUFFLEtBQUssSUFBSSxNQUFNLEVBQTJCLE1BQU0sbUJBQW1CLENBQUE7QUFDNUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQ2pELE9BQU8sRUFBRSxXQUFXLElBQUksWUFBWSxFQUF1QyxNQUFNLHlCQUF5QixDQUFBO0FBQzFHLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFcEMsY0FBYyxlQUFlLENBQUE7QUFDN0IsOENBQThDO0FBQzlDLE9BQU8sRUFBRSxnQkFBZ0IsSUFBSSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFFL0QsY0FBYyxtQkFBbUIsQ0FBQTtBQUNqQyxPQUFPLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBRXRELE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDdEQsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDOUcsT0FBTyxFQUFFLEdBQUcsRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUV6RCxjQUFjLG1CQUFtQixDQUFBO0FBQ2pDLGNBQWMsaUJBQWlCLENBQUE7QUFDL0IsY0FBYyx5QkFBeUIsQ0FBQTtBQUN2QyxjQUFjLHFCQUFxQixDQUFBO0FBQ25D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
|
package/package.json
CHANGED
package/src/Schema.ts
CHANGED
|
@@ -65,17 +65,39 @@ export const PhoneNumber = PhoneNumberT
|
|
|
65
65
|
|
|
66
66
|
export type PhoneNumber = PhoneNumberType
|
|
67
67
|
|
|
68
|
+
// Copied from SchemaAST.collectSentinels (marked @internal in effect).
|
|
69
|
+
// Returns all { key, literal } pairs that can discriminate a union member.
|
|
68
70
|
const getTagFromAST = (schema: S.Top): string => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
const sentinels = collectSentinelsFromAST(schema.ast)
|
|
72
|
+
const sentinel = sentinels.find((s) => s.key === "_tag")
|
|
73
|
+
if (sentinel !== undefined && typeof sentinel.literal === "string") return sentinel.literal
|
|
74
|
+
throw new Error("No _tag literal found on schema member")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectSentinelsFromAST(
|
|
78
|
+
ast: SchemaAST.AST
|
|
79
|
+
): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> {
|
|
80
|
+
switch (ast._tag) {
|
|
81
|
+
case "Declaration": {
|
|
82
|
+
const s = ast.annotations?.["~sentinels"]
|
|
83
|
+
return Array.isArray(s) ? s : []
|
|
76
84
|
}
|
|
85
|
+
case "Objects":
|
|
86
|
+
return ast.propertySignatures.flatMap(
|
|
87
|
+
(ps): Array<{ key: PropertyKey; literal: SchemaAST.LiteralValue | symbol }> => {
|
|
88
|
+
const type = ps.type
|
|
89
|
+
if (!SchemaAST.isOptional(type)) {
|
|
90
|
+
if (SchemaAST.isLiteral(type)) return [{ key: ps.name, literal: type.literal }]
|
|
91
|
+
if (SchemaAST.isUniqueSymbol(type)) return [{ key: ps.name, literal: type.symbol }]
|
|
92
|
+
}
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
case "Suspend":
|
|
97
|
+
return collectSentinelsFromAST(ast.thunk())
|
|
98
|
+
default:
|
|
99
|
+
return []
|
|
77
100
|
}
|
|
78
|
-
throw new Error("No _tag literal found on schema member")
|
|
79
101
|
}
|
|
80
102
|
|
|
81
103
|
export const tags = <
|
package/test/schema.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// import { generateFromArbitrary } from "@effect-app/infra/test"
|
|
2
2
|
import { Array, S } from "effect-app"
|
|
3
|
-
import { expect, test } from "vitest"
|
|
3
|
+
import { expect, expectTypeOf, test } from "vitest"
|
|
4
4
|
|
|
5
5
|
const A = S.Struct({ a: S.NonEmptyString255, email: S.NullOr(S.Email) })
|
|
6
6
|
test("works", () => {
|
|
@@ -54,3 +54,149 @@ test("tagged union derives tag map and tags from v4 literal ast", () => {
|
|
|
54
54
|
expect(isAOrB({ _tag: "B", b: 1 })).toBe(true)
|
|
55
55
|
expect(isAOrB({ _tag: "C", c: true })).toBe(false)
|
|
56
56
|
})
|
|
57
|
+
|
|
58
|
+
test("TaggedUnion tags returns a Literals schema with correct literal values", () => {
|
|
59
|
+
const schema = S.TaggedUnion(
|
|
60
|
+
S.TaggedStruct("X", { x: S.String }),
|
|
61
|
+
S.TaggedStruct("Y", { y: S.Number })
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(schema.tags.literals).toEqual(["X", "Y"])
|
|
65
|
+
expectTypeOf(schema.tags.literals).toMatchTypeOf<readonly ["X", "Y"]>()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("TaggedUnion tags.pick returns a subset of the tag literals", () => {
|
|
69
|
+
const schema = S.TaggedUnion(
|
|
70
|
+
S.TaggedStruct("A", { a: S.String }),
|
|
71
|
+
S.TaggedStruct("B", { b: S.Number }),
|
|
72
|
+
S.TaggedStruct("C", { c: S.Boolean })
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const subset = schema.tags.pick(["A", "C"])
|
|
76
|
+
expect(subset.literals).toEqual(["A", "C"])
|
|
77
|
+
expect(S.decodeSync(subset)("A")).toBe("A")
|
|
78
|
+
expect(S.decodeSync(subset)("C")).toBe("C")
|
|
79
|
+
expect(() => S.decodeUnknownSync(subset)("B")).toThrow()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test("tags standalone function extracts tags from member schemas", () => {
|
|
83
|
+
const members = [
|
|
84
|
+
S.TaggedStruct("Foo", { foo: S.String }),
|
|
85
|
+
S.TaggedStruct("Bar", { bar: S.Number })
|
|
86
|
+
] as const
|
|
87
|
+
|
|
88
|
+
const tagSchema = S.tags(members)
|
|
89
|
+
expect(tagSchema.literals).toEqual(["Foo", "Bar"])
|
|
90
|
+
expect(S.decodeSync(tagSchema)("Foo")).toBe("Foo")
|
|
91
|
+
expect(S.decodeSync(tagSchema)("Bar")).toBe("Bar")
|
|
92
|
+
expect(() => S.decodeUnknownSync(tagSchema)("Baz")).toThrow()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test("ExtendTaggedUnion adds tags to an existing Union", () => {
|
|
96
|
+
const union = S.Union([
|
|
97
|
+
S.TaggedStruct("P", { p: S.String }),
|
|
98
|
+
S.TaggedStruct("Q", { q: S.Number })
|
|
99
|
+
])
|
|
100
|
+
const extended = S.ExtendTaggedUnion(union)
|
|
101
|
+
|
|
102
|
+
expect(extended.tags.literals).toEqual(["P", "Q"])
|
|
103
|
+
expect(S.decodeSync(extended.tags)("P")).toBe("P")
|
|
104
|
+
expect(S.decodeSync(extended.tags)("Q")).toBe("Q")
|
|
105
|
+
expect(() => S.decodeUnknownSync(extended.tags)("R")).toThrow()
|
|
106
|
+
expect(extended.cases["P"].fields._tag.ast.literal).toBe("P")
|
|
107
|
+
expect(extended.guards.P({ _tag: "P", p: "ok" })).toBe(true)
|
|
108
|
+
expect(extended.guards.P({ _tag: "Q", q: 1 })).toBe(false)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("TaggedUnion match dispatches on _tag", () => {
|
|
112
|
+
const schema = S.TaggedUnion(
|
|
113
|
+
S.TaggedStruct("A", { a: S.String }),
|
|
114
|
+
S.TaggedStruct("B", { b: S.Number })
|
|
115
|
+
)
|
|
116
|
+
type T = S.Schema.Type<typeof schema>
|
|
117
|
+
|
|
118
|
+
const matcher = schema.match({
|
|
119
|
+
A: (v) => `got A: ${v.a}`,
|
|
120
|
+
B: (v) => `got B: ${v.b}`
|
|
121
|
+
})
|
|
122
|
+
expect(matcher({ _tag: "A", a: "hello" } as T)).toBe("got A: hello")
|
|
123
|
+
expect(matcher({ _tag: "B", b: 42 } as T)).toBe("got B: 42")
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test("TaggedUnion with single member", () => {
|
|
127
|
+
const schema = S.TaggedUnion(
|
|
128
|
+
S.TaggedStruct("Only", { val: S.String })
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
expect(schema.tags.literals).toEqual(["Only"])
|
|
132
|
+
expect(S.decodeSync(schema.tags)("Only")).toBe("Only")
|
|
133
|
+
expect(() => S.decodeUnknownSync(schema.tags)("Other")).toThrow()
|
|
134
|
+
expect(schema.guards.Only({ _tag: "Only", val: "x" })).toBe(true)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test("TaggedUnion tags type is narrowed to the exact tag literals", () => {
|
|
138
|
+
const schema = S.TaggedUnion(
|
|
139
|
+
S.TaggedStruct("Alpha", { a: S.String }),
|
|
140
|
+
S.TaggedStruct("Beta", { b: S.Number }),
|
|
141
|
+
S.TaggedStruct("Gamma", { c: S.Boolean })
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
type Tags = S.Schema.Type<typeof schema.tags>
|
|
145
|
+
expectTypeOf<Tags>().toEqualTypeOf<"Alpha" | "Beta" | "Gamma">()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test("TaggedUnion with encodeKeys renaming a non-tag key", () => {
|
|
149
|
+
const MemberA = S.TaggedStruct("A", { firstName: S.String }).pipe(
|
|
150
|
+
S.encodeKeys({ firstName: "first_name" })
|
|
151
|
+
)
|
|
152
|
+
const MemberB = S.TaggedStruct("B", { lastName: S.Number }).pipe(
|
|
153
|
+
S.encodeKeys({ lastName: "last_name" })
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
const schema = S.TaggedUnion(MemberA, MemberB)
|
|
157
|
+
|
|
158
|
+
expect(schema.tags.literals).toEqual(["A", "B"])
|
|
159
|
+
expect(S.decodeSync(schema.tags)("A")).toBe("A")
|
|
160
|
+
expect(S.decodeSync(schema.tags)("B")).toBe("B")
|
|
161
|
+
|
|
162
|
+
// decode from encoded (snake_case) to decoded (camelCase)
|
|
163
|
+
const decoded = S.decodeUnknownSync(schema)({ _tag: "A", first_name: "Alice" })
|
|
164
|
+
expect(decoded).toEqual({ _tag: "A", firstName: "Alice" })
|
|
165
|
+
|
|
166
|
+
const decoded2 = S.decodeUnknownSync(schema)({ _tag: "B", last_name: 42 })
|
|
167
|
+
expect(decoded2).toEqual({ _tag: "B", lastName: 42 })
|
|
168
|
+
|
|
169
|
+
// encode back to snake_case
|
|
170
|
+
type T = S.Schema.Type<typeof schema>
|
|
171
|
+
const encoded = S.encodeSync(schema)({ _tag: "A", firstName: "Alice" } as T)
|
|
172
|
+
expect(encoded).toEqual({ _tag: "A", first_name: "Alice" })
|
|
173
|
+
|
|
174
|
+
// guards work on decoded values
|
|
175
|
+
expect(schema.guards.A({ _tag: "A", firstName: "Alice" })).toBe(true)
|
|
176
|
+
expect(schema.guards.A({ _tag: "B", lastName: 42 })).toBe(false)
|
|
177
|
+
expect(schema.guards.B({ _tag: "B", lastName: 42 })).toBe(true)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("TaggedUnion with TaggedClass members", () => {
|
|
181
|
+
class Foo extends S.TaggedClass<Foo>()("Foo", { name: S.String }) {}
|
|
182
|
+
class Bar extends S.TaggedClass<Bar>()("Bar", { count: S.Number }) {}
|
|
183
|
+
|
|
184
|
+
const schema = S.TaggedUnion(Foo, Bar)
|
|
185
|
+
|
|
186
|
+
expect(schema.tags.literals).toEqual(["Foo", "Bar"])
|
|
187
|
+
expect(S.decodeSync(schema.tags)("Foo")).toBe("Foo")
|
|
188
|
+
expect(S.decodeSync(schema.tags)("Bar")).toBe("Bar")
|
|
189
|
+
expect(() => S.decodeUnknownSync(schema.tags)("Baz")).toThrow()
|
|
190
|
+
|
|
191
|
+
const decoded = S.decodeUnknownSync(schema)({ _tag: "Foo", name: "Alice" })
|
|
192
|
+
expect(decoded).toBeInstanceOf(Foo)
|
|
193
|
+
expect(decoded).toEqual(new Foo({ name: "Alice" }))
|
|
194
|
+
|
|
195
|
+
const decoded2 = S.decodeUnknownSync(schema)({ _tag: "Bar", count: 3 })
|
|
196
|
+
expect(decoded2).toBeInstanceOf(Bar)
|
|
197
|
+
expect(decoded2).toEqual(new Bar({ count: 3 }))
|
|
198
|
+
|
|
199
|
+
expect(schema.guards.Foo(new Foo({ name: "Alice" }))).toBe(true)
|
|
200
|
+
expect(schema.guards.Foo(new Bar({ count: 3 }))).toBe(false)
|
|
201
|
+
expect(schema.guards.Bar(new Bar({ count: 3 }))).toBe(true)
|
|
202
|
+
})
|