pelotom / unionize

Boilerplate-free functional sum types in TypeScript
MIT License
402 stars 14 forks source link

Tags are not removed from variant value #67

Open OliverJAsh opened 5 years ago

OliverJAsh commented 5 years ago
import { ofType, unionize } from 'unionize';

const MyUnion = unionize({
    MyVariant: ofType<{ foo: string }>(),
});
type MyUnion = typeof MyUnion._Union;

const myUnion = MyUnion.MyVariant({ foo: 'bar' });
MyUnion.match(myUnion, {
    // Type: { foo: 'bar' }
    // Expected logged value: { foo: 'bar' } (same as type)
    // Actual logged value: { tag: 'MyVariant', foo: 'bar' }
    MyVariant: myVariant => console.log(myVariant),
});

Workaround: if we specify a value, we don't have this issue:

const MyUnion = unionize(
    {
        MyVariant: ofType<{ foo: string }>(),
    },
    { value: 'value' },
);

Why this matters: we are spreading the unionize variant value into a React component:

<div {...myVariant} />

We expect the spreaded object to contain valid props for this React component, which it does, however it also passes the tag prop which is not valid. This results in invalid HTML.

IMO the runtime value should match the type. There should not be an excess property containing the tag—although I realise this would mean creating a new object, in order to remove the tag

pelotom commented 5 years ago

Maybe a better approach would be to leave the runtime semantics alone but to ensure that the type of the refined variant includes the tag property (but only when it will have one, i.e. when there is no value prop.)

OliverJAsh commented 5 years ago

Yeah that could work! Although it won't help much in my specific use case with React, since TS allows excess properties 😢 https://github.com/microsoft/TypeScript/issues/29883