Skip to content

Commit

Permalink
Simplify macro types in TypeScript definition
Browse files Browse the repository at this point in the history
  • Loading branch information
qlonik authored and novemberborn committed Apr 6, 2019
1 parent 9ef456b commit ebf4807
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 95 deletions.
138 changes: 43 additions & 95 deletions index.d.ts
Expand Up @@ -403,66 +403,44 @@ export type Implementation<Context = {}> = (t: ExecutionContext<Context>) => Imp
export type CbImplementation<Context = {}> = (t: CbExecutionContext<Context>) => ImplementationResult;

/** A reusable test or hook implementation. */
export interface Macro<Args extends any[], Context = {}> {
(t: ExecutionContext<Context>, ...args: Args): ImplementationResult;
export type UntitledMacro<Args extends any[], Context = {}> = (t: ExecutionContext<Context>, ...args: Args) => ImplementationResult;

/** A reusable test or hook implementation. */
export type Macro<Args extends any[], Context = {}> = UntitledMacro<Args, Context> & {
/**
* Implement this function to generate a test (or hook) title whenever this macro is used. `providedTitle` contains
* the title provided when the test or hook was declared. Also receives the remaining test arguments.
*/
title?: (providedTitle: string | undefined, ...args: Args) => string;
}

export type EitherMacro<Args extends any[], Context> = Macro<Args, Context> | UntitledMacro<Args, Context>;

/** Alias for a single macro, or an array of macros. */
export type OneOrMoreMacros<Args extends any[], Context> = Macro<Args, Context> | [Macro<Args, Context>, ...Macro<Args, Context>[]];
export type OneOrMoreMacros<Args extends any[], Context> = EitherMacro<Args, Context> | [EitherMacro<Args, Context>, ...EitherMacro<Args, Context>[]];

/** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
export interface CbMacro<Args extends any[], Context = {}> {
(t: CbExecutionContext<Context>, ...args: Args): ImplementationResult;
export type UntitledCbMacro<Args extends any[], Context = {}> = (t: CbExecutionContext<Context>, ...args: Args) => ImplementationResult

/** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
export type CbMacro<Args extends any[], Context = {}> = UntitledCbMacro<Args, Context> & {
title?: (providedTitle: string | undefined, ...args: Args) => string;
}

/** Alias for a single macro, or an array of macros, used for tests & hooks declared with the `.cb` modifier. */
export type OneOrMoreCbMacros<Args extends any[], Context> = CbMacro<Args, Context> | [CbMacro<Args, Context>, ...CbMacro<Args, Context>[]];

/** Infers the types of the additional arguments the macro implementations should be called with. */
export type InferArgs<OneOrMore> =
OneOrMore extends Macro<infer Args, any> ? Args :
OneOrMore extends Macro<infer Args, any>[] ? Args :
OneOrMore extends CbMacro<infer Args, any> ? Args :
OneOrMore extends CbMacro<infer Args, any>[] ? Args :
never;

export type TitleOrMacro<Context> = string | OneOrMoreMacros<any, Context>

export type MacroOrFirstArg<TitleOrMacro, Context> =
TitleOrMacro extends string ? OneOrMoreMacros<any, Context> :
TitleOrMacro extends OneOrMoreMacros<any, Context> ? InferArgs<TitleOrMacro>[0] :
never

export type TitleOrCbMacro<Context> = string | OneOrMoreCbMacros<any, Context>
export type EitherCbMacro<Args extends any[], Context> = CbMacro<Args, Context> | UntitledCbMacro<Args, Context>;

export type CbMacroOrFirstArg<TitleOrMacro, Context> =
TitleOrMacro extends string ? OneOrMoreCbMacros<any, Context> :
TitleOrMacro extends OneOrMoreCbMacros<any, Context> ? InferArgs<TitleOrMacro>[0] :
never

export type RestArgs<TitleOrMacro, MacroOrFirstArg, Context> =
MacroOrFirstArg extends OneOrMoreMacros<any, Context> ? InferArgs<MacroOrFirstArg> :
MacroOrFirstArg extends OneOrMoreCbMacros<any, Context> ? InferArgs<MacroOrFirstArg> :
TitleOrMacro extends OneOrMoreMacros<any, Context> ? Tail<InferArgs<TitleOrMacro>> :
TitleOrMacro extends OneOrMoreCbMacros<any, Context> ? Tail<InferArgs<TitleOrMacro>> :
never
/** Alias for a single macro, or an array of macros, used for tests & hooks declared with the `.cb` modifier. */
export type OneOrMoreCbMacros<Args extends any[], Context> = EitherCbMacro<Args, Context> | [EitherCbMacro<Args, Context>, ...EitherCbMacro<Args, Context>[]];

export interface TestInterface<Context = {}> {
/** Declare a concurrent test. */
(title: string, implementation: Implementation<Context>): void;

/** Declare a concurrent test that uses one or more macros. Additional arguments are passed to the macro. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void

/** Declare a concurrent test that uses one or more macros. The macro is responsible for generating a unique test title. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that is run once, after all tests have passed. */
after: AfterInterface<Context>;
Expand Down Expand Up @@ -499,10 +477,10 @@ export interface AfterInterface<Context = {}> {
(title: string, implementation: Implementation<Context>): void;

/** Declare a hook that is run once, after all tests have passed. Additional arguments are passed to the macro. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that is run once, after all tests have passed. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that is run once, after all tests are done. */
always: AlwaysInterface<Context>;
Expand All @@ -521,10 +499,10 @@ export interface AlwaysInterface<Context = {}> {
(title: string, implementation: Implementation<Context>): void;

/** Declare a hook that is run once, after all tests are done. Additional arguments are passed to the macro. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that is run once, after all tests are done. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that must call `t.end()` when it's done. */
cb: HookCbInterface<Context>;
Expand All @@ -540,10 +518,10 @@ export interface BeforeInterface<Context = {}> {
(title: string, implementation: Implementation<Context>): void;

/** Declare a hook that is run once, before all tests. Additional arguments are passed to the macro. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that is run once, before all tests. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a hook that must call `t.end()` when it's done. */
cb: HookCbInterface<Context>;
Expand All @@ -559,13 +537,13 @@ export interface CbInterface<Context = {}> {
* Declare a concurrent test that uses one or more macros. The macros must call `t.end()` when they're done.
* Additional arguments are passed to the macro.
*/
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/**
* Declare a concurrent test that uses one or more macros. The macros must call `t.end()` when they're done.
* The macro is responsible for generating a unique test title.
*/
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/** Declare a test that is expected to fail. */
failing: CbFailingInterface<Context>;
Expand All @@ -582,13 +560,13 @@ export interface CbFailingInterface<Context = {}> {
* Declare a test that uses one or more macros. The macros must call `t.end()` when they're done.
* Additional arguments are passed to the macro. The test is expected to fail.
*/
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/**
* Declare a test that uses one or more macros. The macros must call `t.end()` when they're done.
* The test is expected to fail.
*/
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

only: CbOnlyInterface<Context>;
skip: CbSkipInterface<Context>;
Expand All @@ -604,24 +582,24 @@ export interface CbOnlyInterface<Context = {}> {
* Declare a test that uses one or more macros. The macros must call `t.end()` when they're done.
* Additional arguments are passed to the macro. Only this test and others declared with `.only()` are run.
*/
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/**
* Declare a test that uses one or more macros. The macros must call `t.end()` when they're done.
* Additional arguments are passed to the macro. Only this test and others declared with `.only()` are run.
*/
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
}

export interface CbSkipInterface<Context = {}> {
/** Skip this test. */
(title: string, implementation: CbImplementation<Context>): void;

/** Skip this test. */
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/** Skip this test. */
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
}

export interface FailingInterface<Context = {}> {
Expand All @@ -632,13 +610,13 @@ export interface FailingInterface<Context = {}> {
* Declare a concurrent test that uses one or more macros. Additional arguments are passed to the macro.
* The test is expected to fail.
*/
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/**
* Declare a concurrent test that uses one or more macros. The macro is responsible for generating a unique test title.
* The test is expected to fail.
*/
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

only: OnlyInterface<Context>;
skip: SkipInterface<Context>;
Expand All @@ -655,12 +633,12 @@ export interface HookCbInterface<Context = {}> {
* Declare a hook that uses one or more macros. The macros must call `t.end()` when they're done.
* Additional arguments are passed to the macro.
*/
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/**
* Declare a hook that uses one or more macros. The macros must call `t.end()` when they're done.
*/
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

skip: HookCbSkipInterface<Context>;
}
Expand All @@ -673,10 +651,10 @@ export interface HookCbSkipInterface<Context = {}> {
(title: string, implementation: CbImplementation<Context>): void;

/** Skip this hook. */
<ToM extends TitleOrCbMacro<Context>, MoA extends CbMacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;

/** Skip this hook. */
(macro: OneOrMoreCbMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
}

export interface HookSkipInterface<Context = {}> {
Expand All @@ -687,10 +665,10 @@ export interface HookSkipInterface<Context = {}> {
(title: string, implementation: Implementation<Context>): void;

/** Skip this hook. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Skip this hook. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;
}

export interface OnlyInterface<Context = {}> {
Expand All @@ -701,26 +679,26 @@ export interface OnlyInterface<Context = {}> {
* Declare a test that uses one or more macros. Additional arguments are passed to the macro.
* Only this test and others declared with `.only()` are run.
*/
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/**
* Declare a test that uses one or more macros. The macro is responsible for generating a unique test title.
* Only this test and others declared with `.only()` are run.
*/
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;
}

export interface SerialInterface<Context = {}> {
/** Declare a serial test. */
(title: string, implementation: Implementation<Context>): void;

/** Declare a serial test that uses one or more macros. Additional arguments are passed to the macro. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/**
* Declare a serial test that uses one or more macros. The macro is responsible for generating a unique test title.
*/
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Declare a serial hook that is run once, after all tests have passed. */
after: AfterInterface<Context>;
Expand Down Expand Up @@ -750,10 +728,10 @@ export interface SkipInterface<Context = {}> {
(title: string, implementation: Implementation<Context>): void;

/** Skip this test. */
<ToM extends TitleOrMacro<Context>, MoA extends MacroOrFirstArg<ToM, Context>>(titleOrMacro: ToM, macroOrArg: MoA, ...rest: RestArgs<ToM, MoA, Context>): void;
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;

/** Skip this test. */
(macro: OneOrMoreMacros<[], Context>): void
<T extends any[]>(title: string, macros: OneOrMoreMacros<T, Context>, ...rest: T): void;
}

export interface TodoDeclaration {
Expand Down Expand Up @@ -804,33 +782,3 @@ export const todo: TodoDeclaration;

/** Meta data associated with the current process. */
export const meta: MetaInterface;

/*
Tail type from <https://github.com/tycho01/typical/blob/25f11ed92c960aab1ebbf47fd6ead9e0ae51d947/src/array/Tail.ts>.
MIT License
Copyright (c) 2017 Thomas Crockett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/** Get all but the first element of a tuple. */
export type Tail<T extends any[]> =
((...args: T) => any) extends ((head: any, ...tail: infer R) => any) ? R : never;
11 changes: 11 additions & 0 deletions test/ts-types/macros.ts
Expand Up @@ -62,3 +62,14 @@ import test, {ExecutionContext, Macro} from '../..';
t.is(input.length, expected)
}, 'bar', 3)
}

// Completely infer parameters
{
test('has length 3', (t, input, expected) => {
t.is(input.length, expected);
}, 'foo', 3);

test((t, input, expected) => {
t.is(input.length, expected)
}, 'foo', 3);
}

0 comments on commit ebf4807

Please sign in to comment.