Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/plain-variant-unions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Include plain variant structs in the default variant union.
28 changes: 18 additions & 10 deletions packages/effect/src/unstable/schema/VariantSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,13 @@ type MissingSelfGeneric<Params extends string = ""> =
* @category models
* @since 4.0.0
*/
export interface Union<Members extends ReadonlyArray<Struct<any>>> extends
Schema.Union<
{
readonly [K in keyof Members]: [Members[K]] extends [Schema.Top] ? Members[K] : never
}
>
export interface Union<Members extends ReadonlyArray<Struct<any>>, Default extends string = string>
extends
Schema.Union<
{
readonly [K in keyof Members]: Extract<Default, Members[K], true>
}
>
{}

/**
Expand Down Expand Up @@ -410,7 +411,7 @@ export const make = <
}
readonly Union: <const Members extends ReadonlyArray<Struct<any>>>(
members: Members
) => Union<Members> & Union.Variants<Members, Variants[number]>
) => Union<Members, Default> & Union.Variants<Members, Variants[number]>
readonly extract: {
<V extends Variants[number]>(
variant: V
Expand Down Expand Up @@ -469,7 +470,7 @@ export const make = <
}
}
function UnionVariants(members: ReadonlyArray<Struct<any>>) {
return Union(members, options.variants)
return Union(members, options.defaultVariant, options.variants)
}
const fieldEvolve = dual(
2,
Expand Down Expand Up @@ -582,11 +583,18 @@ const Field = <const A extends Field.Config>(schemas: A): Field<A> => {
return self
}

const Union = <Members extends ReadonlyArray<Struct<any>>, Variants extends ReadonlyArray<string>>(
const Union = <
Members extends ReadonlyArray<Struct<any>>,
Default extends string,
Variants extends ReadonlyArray<string>
>(
members: Members,
defaultVariant: Default,
variants: Variants
) => {
const VariantUnion = Schema.Union(members.filter((member) => Schema.isSchema(member))) as any
const VariantUnion = Schema.Union(
members.map((member) => Schema.isSchema(member) ? member : extract(member, defaultVariant, { isDefault: true }))
) as any
for (const variant of variants) {
Object.defineProperty(VariantUnion, variant, {
value: Schema.Union(members.map((member) => extract(member, variant)))
Expand Down
11 changes: 11 additions & 0 deletions packages/effect/test/unstable/schema/VariantSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ describe("VariantSchema", () => {
assert.deepStrictEqual(Schema.decodeSync(User.b)({ name: "Alice" }), { name: "Alice" })
assert.deepStrictEqual(Object.keys(User.fields), ["id", "name"])
})

it("includes plain variant structs in the default union", () => {
const Test = VariantSchema.make({ variants: ["a", "b"], defaultVariant: "a" })
const first = Test.Struct({ value: Schema.String })
const second = Test.Struct({ value: Schema.Number })
const union = Test.Union([first, second])

assert.strictEqual(union.members.length, 2)
assert.deepStrictEqual(Schema.decodeUnknownSync(union)({ value: "foo" }), { value: "foo" })
assert.deepStrictEqual(Schema.decodeUnknownSync(union)({ value: 42 }), { value: 42 })
})
Comment thread
tim-smart marked this conversation as resolved.
})

describe("Model", () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/effect/typetest/VariantSchema.tst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ describe("VariantSchema", () => {
const second = Test.Struct({
value: Test.FieldOnly(["a", "b"])(Schema.Number)
})
const union = Test.Union([first, second])

expect(Test.Union).type.toBeCallableWith([first, second])
expect(Test.Union).type.not.toBeCallableWith(first, second)
expect<Schema.Schema.Type<typeof union>>().type.toBe<
{ readonly value: string } | { readonly value: number }
>()
})

it("Class preserves constructor and variant schema types", () => {
Expand Down
Loading