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

Cover combineEvents method with type tests and fix type safety #93

Merged
merged 2 commits into from
Sep 27, 2020
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
25 changes: 13 additions & 12 deletions combine-events/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Unit, Store, Event, Effect } from 'effector';

type Tuple<T = unknown> = [T] | T[];
type Results = { [key: string]: any } | Tuple<any>;
type Shape = Record<string, unknown> | Tuple;

type Events<Result> = {
[Key in keyof Result]: Event<Result[Key]>;
};
Expand All @@ -10,26 +11,26 @@ type ReturnTarget<Result, Target> = Target extends Store<infer S>
? S extends Result
? Store<S>
: Store<Result>
: Target extends Event<infer E>
? E extends Result
? Event<E>
: Target extends Event<infer P>
? P extends Result
? Event<P>
: Event<Result>
: Target extends Effect<infer P, infer D, infer F>
? P extends Result
? Effect<P, D, F>
: Effect<Result, D, F>
: Unit<Result>;

export function combineEvents<T extends Results>(config: {
events: Events<T>;
export function combineEvents<P extends Shape>(config: {
events: Events<P>;
reset?: Unit<any>;
}): Event<T>;
}): Event<P>;

export function combineEvents<
T extends Results,
Target extends Unit<Results>
P extends Shape,
T extends Unit<P extends Tuple ? P : Partial<P>>
>(config: {
events: Events<T>;
target: Target;
events: Events<P>;
target: T;
reset?: Unit<any>;
}): ReturnTarget<T, Target>;
}): ReturnTarget<P, T>;
262 changes: 262 additions & 0 deletions test-typings/combine-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import { expectType } from 'tsd';
import {
Event,
Store,
Effect,
createEvent,
createStore,
createEffect,
} from 'effector';
import { combineEvents } from '../combine-events';

// Check simple combine of different events
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

interface Target {
foo: string;
bar: number;
baz: boolean;
}

const target = combineEvents({
events: { foo, bar, baz },
});

expectType<Event<Target>>(target);

// @ts-expect-error
const _fail1: Event<{ foo: string; bar: number }> = target;

// @ts-expect-error
const _fail2: Event<{ foo: string; bar: number; baz: number }> = target;
}

// With object
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();
const bai = createEvent<{ foo: string }>();

interface Target {
foo: string;
bar: number;
baz: boolean;
bai: { foo: string };
}

const target = combineEvents({
events: { foo, bar, baz, bai },
});

expectType<Event<Target>>(target);

// @ts-expect-error
const _fail1: Event<{ foo: string; bar: number }> = target;

// @ts-expect-error
const _fail2: Event<{ foo: string; bar: number; baz: number }> = target;

// @ts-expect-error
const _fail2: Event<{
foo: string;
bar: number;
baz: number;
bai: { demo: string };
}> = target;
}

// Array combiner
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

type Target = [string, number, boolean];

const target = combineEvents({
events: [foo, bar, baz],
});

expectType<Event<Target>>(target);

// @ts-expect-error
const _fail1: Event<[string, number]> = target;

// @ts-expect-error
const _fail2: Event<[string, number, number]> = target;
}

// With target event

// Check simple combine of different events
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

const target = createEvent<{
foo: string;
bar: number;
baz: boolean;
}>();

combineEvents({
events: { foo, bar, baz },
target,
});

combineEvents({
events: { foo, bar, baz },
target: createEvent<{ foo: string; bar: number }>(),
});

// @ts-expect-error
combineEvents({
events: { foo, bar, baz },
target: createEvent<{ foo: string; bar: number; baz: number }>(),
});
}

// With object
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();
const bai = createEvent<{ foo: string }>();

const target = createEvent<{
foo: string;
bar: number;
baz: boolean;
bai: { foo: string };
}>();

combineEvents({
events: { foo, bar, baz, bai },
target,
});

combineEvents({
events: { foo, bar, baz, bai },
target: createEvent<{ foo: string; bar: number }>(),
});

// @ts-expect-error
combineEvents({
events: { foo, bar, baz, bai },
target: createEvent<{ foo: string; bar: number; baz: number }>(),
});

// @ts-expect-error
combineEvents({
events: { foo, bar, baz, bai },
target: createEvent<{
foo: string;
bar: number;
baz: number;
bai: { demo: string };
}>(),
});
}

// Array combiner
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

const target = createEvent<[string, number, boolean]>();

combineEvents({
events: [foo, bar, baz],
target,
});

// @ts-expect-error
combineEvents({
events: [foo, bar, baz],
target: createEvent<[string, number, number]>(),
});

// @ts-expect-error
combineEvents({
events: [foo, bar, baz],
target: createEvent<[string]>(),
});
}

// Check target Event
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

interface Target {
foo: string;
bar: number;
baz: boolean;
}

const target = createEvent<Target>();

const event = combineEvents({
events: { foo, bar, baz },
target,
});

expectType<Event<Target>>(event);
}

// Check target Store
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

interface Target {
foo: string;
bar: number;
baz: boolean;
}

const target = createStore<Target>({
foo: 'string',
bar: 1,
baz: true,
});

const store = combineEvents({
events: { foo, bar, baz },
target,
});

expectType<Store<Target>>(store);
}

// Check target Effect
{
const foo = createEvent<string>();
const bar = createEvent<number>();
const baz = createEvent<boolean>();

interface Target {
foo: string;
bar: number;
baz: boolean;
}

const target = createEffect<Target, void>({
handler: () => {},
});

const effect = combineEvents({
events: { foo, bar, baz },
target,
});

expectType<Effect<Target, void>>(effect);
}