Skip to content

Commit

Permalink
few comments (#36)
Browse files Browse the repository at this point in the history
Allow including tag in value type
  • Loading branch information
sledorze authored and pelotom committed May 8, 2018
1 parent d8f7f95 commit b9512a0
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 17 deletions.
13 changes: 13 additions & 0 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ describe('merged', () => {
expect(T.foo(input).conflict).toBe('foo');
});
});

describe('can define an interface tag', () => {
it('generate a type without an intersection', () => {
interface Foo {
tag: 'foo';
x: number;
}
const T = unionize({
foo: ofType<Foo>(),
});
expect(T.foo({ x: 42 }).tag).toBe('foo');
});
});
});

describe('separate', () => {
Expand Down
43 changes: 26 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
export type Unionized<Record, TaggedRecord> = {
export type Unionized<Record, TaggedRecord, TagProp extends string> = {
_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]>;
} & Creators<Record, TaggedRecord>;
} & Creators<Record, TaggedRecord, TagProp>;

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

export type Creators<Record, TaggedRecord> = {
[T in keyof Record]: {} extends Record[T]
export type Creators<Record, TaggedRecord, TagProp extends string> = {
[T in keyof Record]: {} extends UnTagged<Record[T], TagProp>
? ((value?: {}) => TaggedRecord[keyof TaggedRecord])
: ((value: Record[T]) => TaggedRecord[keyof TaggedRecord])
: ((value: UnTagged<Record[T], TagProp>) => TaggedRecord[keyof TaggedRecord])
};

export type Predicates<TaggedRecord> = {
Expand All @@ -32,32 +32,41 @@ export type MatchCases<Record, Union, A> =
| Cases<Record, A> & NoDefaultProp
| Partial<Cases<Record, A>> & { default: (variant: Union) => A };

export type Match<Record, Union> = {
export interface Match<Record, Union> {
<A>(cases: MatchCases<Record, Union, A>): (variant: Union) => A;
<A>(variant: Union, cases: MatchCases<Record, Union, A>): A;
};
}

export type TransformCases<Record, Union> = Partial<
{ [T in keyof Record]: (value: Record[T]) => Union }
>;

export type Transform<Record, Union> = {
export interface Transform<Record, Union> {
(cases: TransformCases<Record, Union>): (variant: Union) => Union;
(variant: Union, cases: TransformCases<Record, Union>): Union;
};
}

export type MultiValueVariants<Record extends MultiValueRec, TagProp extends string> = {
[T in keyof Record]: { [_ in TagProp]: T } & Record[T]
[T in keyof Record]: Record[T] extends { [_ in TagProp]: T } // does record already has tag with correct value?
? Record[T] // yes: return as is
: { [_ in TagProp]: T } & Record[T] // no: decorate with tag
};

export type UnTagged<Record, TagProp extends string = 'tag'> = Pick<
Record,
{ [k in keyof Record]: k extends TagProp ? never : k }[keyof Record]
>;

export type SingleValueVariants<
Record extends SingleValueRec,
TagProp extends string,
ValProp extends string
> = { [T in keyof Record]: { [_ in TagProp]: T } & { [_ in ValProp]: Record[T] } };

// Forbid usage of default property; reserved for pattern matching.
export type NoDefaultProp = { default?: never };
export interface NoDefaultProp {
default?: never;
}

export type SingleValueRec = NoDefaultRec<{} | null>;
export type MultiValueRec = NoDefaultRec<{ [tag: string]: any }>;
Expand All @@ -84,15 +93,15 @@ export function unionize<
>(
record: Record,
config: { value: ValProp; tag?: TagProp },
): Unionized<Record, SingleValueVariants<Record, TagProp, ValProp>>;
): Unionized<Record, SingleValueVariants<Record, TagProp, ValProp>, TagProp>;
export function unionize<Record extends MultiValueRec, TagProp extends string = 'tag'>(
record: Record,
config?: { tag: TagProp },
): Unionized<Record, MultiValueVariants<Record, TagProp>>;
): Unionized<Record, MultiValueVariants<Record, TagProp>, TagProp>;
export function unionize<Record>(record: Record, config?: { value?: string; tag?: string }) {
const { value: valProp = undefined, tag: tagProp = 'tag' } = config || {};

const creators = {} as Creators<Record, any>;
const creators = {} as Creators<Record, any, any>;
for (const tag in record) {
creators[tag] = ((value = {}) =>
valProp ? { [tagProp]: tag, [valProp]: value } : { ...value, [tagProp]: tag }) as any;
Expand Down

0 comments on commit b9512a0

Please sign in to comment.