question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can't use masked and union together

See original GitHub issue

Let’s say I want to define a struct for user events, using a discriminated union. Sample input data looks like:

const inputCreated = {
    eventType: 'USER_CREATED',
    userId: 1234,
    extraField: 'ignore me',
};

const inputUnsubscribed = {
    eventType: 'UNSUBSCRIBE',
    email: 'foo@example.com`,
    extraField: 'ignore me',
};

I might start off with this struct definition:

const EventStruct1 = s.union([
    s.type({
        eventType: s.literal('USER_CREATED'),
        userId: s.number(),
    }),
    s.type({
        eventType: s.literal('UNSUBSCRIBE'),
        email: s.string(),
    }),
]);

Which works, but keeps the extraField:

const event = s.create(inputCreated, EventStruct1);
console.log(event); // contains extraField

So I try to replace type with object and use masked:

const EventStruct2 = s.union([
    s.masked(
        s.object({
            eventType: s.literal('USER_CREATED'),
            userId: s.number(),
        }),
    ),
    s.masked(
        s.object({
            eventType: s.literal('UNSUBSCRIBE'),
            email: s.string(),
        }),
    ),
]);

Now this code throws an error (and a pretty cryptic error at that):

// Throws: Expected the value to satisfy a union of `object | object`, but received: [object Object]
const event = s.create(inputCreated, EventStruct2);

Moving the masked outside the union doesn’t work either:

const EventStruct3 = s.masked(
    s.union([
        s.object({
            eventType: s.literal('USER_CREATED'),
            userId: s.number(),
        }),
        s.object({
            eventType: s.literal('UNSUBSCRIBE'),
            email: s.string(),
        }),
    ]),
);

// Throws: Expected the value to satisfy a union of `object | object`, but received: [object Object]
const event = s.create(inputCreated, EventStruct3);

Is there a way to implement a union where we strip out unknown properties?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
dlindenkreuzcommented, Mar 23, 2021

Workaround for now:

const oneOf = <T extends Struct<any, any>[]>(expected: T) =>
  define<T[number]["TYPE"]>("oneOf", (v) => expected.some((t) => t.is(v)))

const fooType = oneOf([
  s.string(),
  s.type({ 
    value: s.string()
  })
])

fooType.create("hi")
fooType.create({ value: "hi" })
fooType.create({ fail: "nope" }) // throws

Edit: if you need to coerce types, swap out the validator with

const oneOf = <T extends Struct<any, any>[]>(expected: T) =>
  define<T[number]["TYPE"]>("oneOf", (v) =>
    expected.some((t) => t.validate(v, { coerce: true })[1]))
1reaction
ianstormtaylorcommented, Jan 12, 2021

Hey @stevecaldwell77 thanks for opening this. I see what you mean. This is due to union not allowing for any coercion logic right now. I wasn’t exactly sure how to handle this, since running coercions against non-similar potential types seems weird, but in this case it seems weird not to.

I’m open to allowing unions to have coercion though because this seems like a very reasonable use case. Would you be down to PR it? Thanks!

Read more comments on GitHub >

github_iconTop Results From Across the Web

7 Reasons Why Union Is Not Working In Inkscape | Path ...
Reason #1: Your Objects Are Grouped Together · Reason #2: Text Objects · Reason #3: Strokes and Lines · Reason #4: Clipped Objects...
Read more >
Strong teachers unions and school mask mandates go ...
Earlier this year, Gov. Kim Reynolds (R) signed a bill that prohibits local districts from requiring masks inside schools.
Read more >
SQL UNION overview, usage and examples
This article provides overview of the SQL UNION operator, along with examples and explore some common questions like the differences between ...
Read more >
Typescript property does not exist on union type - Stack Overflow
You have to narrow down the type. You can do so by using the in operator. const getText = (obj: Obj1 | Obj2):...
Read more >
Select Mask Content (Intersection rather than Union) in Inkscape
When creating a mask, the resulting cut-out will have the selection size of the object which has parts "cut off" via the mask....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found