Skip to content

Instantly share code, notes, and snippets.

@dwjohnston
Created June 27, 2024 00:41
Show Gist options
  • Select an option

  • Save dwjohnston/0a0ddc58190a5caefe387d89019d61d2 to your computer and use it in GitHub Desktop.

Select an option

Save dwjohnston/0a0ddc58190a5caefe387d89019d61d2 to your computer and use it in GitHub Desktop.

Revisions

  1. dwjohnston created this gist Jun 27, 2024.
    67 changes: 67 additions & 0 deletions MockedService.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,67 @@
    /* eslint-disable @typescript-eslint/no-unused-vars */
    /* eslint-disable @typescript-eslint/no-explicit-any */
    type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
    type IsAny<T, Y, N> = IfAny<T, Y, N>;

    type TypeWithGeneric<T> = T[];

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    // type extractGeneric<Type> =
    // Type extends jest.MaybeMockedDeep<infer X, any[], any> ? X : never;

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    type JestMockAny = jest.Mock<any, any, any>;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    type JestMockString = jest.Mock<string, number[], null>;

    type IsJestMockAny<T extends jest.MockableFunction> = IsAny<
    ReturnType<T>,
    never,
    jest.MaybeMockedDeep<T>
    >;

    const fn = jest.fn<string, number[], any>();

    const result = fn(1, 2, 3);

    type A = IsJestMockAny<JestMockAny>;
    type B = IsJestMockAny<JestMockString>;

    const b: B = (a, b) => {};

    export type MockedService<
    T extends Record<string | symbol | number, jest.MockableFunction>
    > = {
    // https://stackoverflow.com/questions/78674994/is-there-way-to-say-but-doesnt-match-any-in-typescript/78675024#78675024
    [K in keyof T]: IsJestMockAny<T[K]>;
    } & T;

    type Foo = {
    getA: (a: string, b: string) => Promise<{ c: string }>;
    getB: (e: number, f: number) => Promise<{ g: string }>;
    };

    const mockedFoo: MockedService<Foo> = {
    getA: jest.fn().mockRejectedValue(new Error('not implemented')),

    // nb. The mock return values are not stricted enforced, if you
    // don't wrap with jest.mocked!
    getB: jest.fn()
    };

    function createMockedService<
    T extends Record<string | symbol | number, jest.MockableFunction>,
    V extends MockedService<T>
    >(value: V) {
    return value;
    }

    createMockedService<Foo>({
    getA: jest.fn().mockRejectedValue(new Error('not implemented')),

    // nb. The mock return values are not stricted enforced, if you
    // don't wrap with jest.mocked!
    getB: jest.fn()
    });

    mockedFoo.getB(1, 2);