Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnionOf does not work. #34

Closed
sledorze opened this issue May 4, 2018 · 2 comments
Closed

UnionOf does not work. #34

sledorze opened this issue May 4, 2018 · 2 comments

Comments

@sledorze
Copy link
Contributor

sledorze commented May 4, 2018

it generates a variance issue (typescript 2.8.3).

Code

  describe('unionOf', () => {
    it('should be usable', () => {
      const T = unionize(
        {
          foo: ofType<{ x: number }>(),
        }
      );
      type ActionType = UnionOf<typeof T> // Error message here
    });
  });

Error message:

[ts]
Type 'Unionized<{ foo: { x: number; }; }, MultiValueVariants<{ foo: { x: number; }; }, "tag">>' does not satisfy the constraint 'Unionized<any, any>'.
  Type 'Unionized<{ foo: { x: number; }; }, MultiValueVariants<{ foo: { x: number; }; }, "tag">>' is not assignable to type '{ _Tags: string; _Record: any; _Union: any; is: Predicates<any>; as: Casts<any, any>; match: Matc...'.
    Types of property 'match' are incompatible.
      Type 'Match<{ foo: { x: number; }; }, { tag: "foo"; } & { x: number; }>' is not assignable to type 'Match<any, any>'.
        Types of parameters 'cases' and 'cases' are incompatible.
          Type 'MatchCases<any, any, any>' is not assignable to type 'MatchCases<{ foo: { x: number; }; }, { tag: "foo"; } & { x: number; }, any>'.
            Type 'Cases<any, any> & NoDefaultProp' is not assignable to type 'MatchCases<{ foo: { x: number; }; }, { tag: "foo"; } & { x: number; }, any>'.
              Type 'Cases<any, any> & NoDefaultProp' is not assignable to type 'Partial<Cases<{ foo: { x: number; }; }, any>> & { default: (variant: { tag: "foo"; } & { x: numbe...'.
                Type 'Cases<any, any> & NoDefaultProp' is not assignable to type '{ default: (variant: { tag: "foo"; } & { x: number; }) => any; }'.
                  Types of property 'default' are incompatible.
                    Type 'undefined' is not assignable to type '(variant: { tag: "foo"; } & { x: number; }) => any'.

The pattern used by UnionOf works well with interfaces but not with types (variance handling is not the same I guess).

Union cannot be made an interface because it mixes Creators which cannot be made an interface.

One possible solution would be to nest Creators inside a field of Union, like so:

export interface Unionized<Record, TaggedRecord> {
  _Tags: keyof TaggedRecord;
  _Record: Record;
  _Union: TaggedRecord[keyof TaggedRecord];
  is: Predicates<TaggedRecord>;
  as: Casts<Record, TaggedRecord[keyof TaggedRecord]>;
  match: Match<Record, TaggedRecord[keyof TaggedRecord]>;
  transform: Transform<Record, TaggedRecord[keyof TaggedRecord]>;
  create: Creators<Record, TaggedRecord>
}

(obviously you need to change the way creators are mixed into the related function)
In that case it works, however that's a breaking change.

@pelotom
Copy link
Owner

pelotom commented May 8, 2018

Gah, I really need to add type tests to this project.

@sledorze
Copy link
Contributor Author

sledorze commented May 9, 2018

I have a quick fix:

export type Unionized<Record, TaggedRecord, TagProp extends string> = UnionTypes<
  Record,
  TaggedRecord
> &
  Creators<Record, TaggedRecord, TagProp> &
  UnionExtensions<Record, TaggedRecord>;

export interface UnionTypes<Record, TaggedRecord> {
  _Tags: keyof TaggedRecord;
  _Record: Record;
  _Union: TaggedRecord[keyof TaggedRecord];
}
export interface UnionExtensions<Record, TaggedRecord> {
  is: Predicates<TaggedRecord>;
  as: Casts<Record, TaggedRecord[keyof TaggedRecord]>;
  match: Match<Record, TaggedRecord[keyof TaggedRecord]>;
  transform: Transform<Record, TaggedRecord[keyof TaggedRecord]>;
}

export type TagsOf<U extends UnionTypes<any, any>> = U['_Tags'];
export type RecordOf<U extends UnionTypes<any, any>> = U['_Record'];
export type UnionOf<U extends UnionTypes<any, any>> = U['_Union'];

Ok for a PR?
#38

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants