vitest-dev / vitest

Next generation testing framework powered by Vite.
https://vitest.dev
MIT License
12.69k stars 1.14k forks source link

MockedFunction type doesn't overlap the type of generic functions #1781

Open danielelkington opened 2 years ago

danielelkington commented 2 years ago

Describe the bug

Sometimes we need to extend a TypeScript interface, replacing functions with mocked versions (that are eventually created with vi.fn()). This works with most functions, but not with generic functions. It used to work up until Vitest 0.15.2, but broke in 0.16.0 and is still broken as-of 0.20.3.

Reproduction

import { MockedFunction } from 'vitest'

interface MyInterface {
  someFunction<T>(a: number): T
}

// TS error on the next line from Vitest 0.16.0:
// Interface 'MyMockedInterface' incorrectly extends interface 'MyInterface'.
// The types returned by 'someFunction(...)' are incompatible between these types
interface MyMockedInterface extends MyInterface {
  someFunction: MockedFunction<MyInterface['someFunction']>
}

System Info

System:
    OS: Windows 10 10.0.19044
    CPU: (8) x64 Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
    Memory: 2.90 GB / 15.81 GB
  Binaries:
    Node: 16.14.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.18 - C:\Program Files\nodejs\yarn.CMD
    npm: 8.5.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 103.0.5060.114
    Edge: Spartan (44.19041.1266.0), Chromium (103.0.1264.77)
    Internet Explorer: 11.0.19041.1566
  npmPackages:
    @vitejs/plugin-vue: ^2.2.0 => 2.2.4
    vite: ^2.9.12 => 2.9.12
    vitest: ^0.16.0 => 0.16.0

Used Package Manager

npm

Validations

sheremet-va commented 2 years ago

Don't use MockedFunction as a type (it's not a return type for vi.fn). Use Mock/MockInstance. We should remove these types from exports I guess. They are for MaybeMocked.


Hm, I got confused, never mind the above. Still, I don't see types working as you expect in 0.15.2. Are you sure you are not updating typescript at the same time as Vitest?

danielelkington commented 2 years ago

If I downgrade to Vitest 0.15.2 but leave everything else the same, the code in the reproduction works. Note that I have to restart the TypeScript server or else restart Visual Studio Code to refresh everything after downgrading the Vitest version.

KevBeltrao commented 4 months ago

This error is because of the mismatch between the return values of both someFunction(). I believe they should have the same return type. I didn't try this code, but have you considered adding the generic to MyMockedInterface's someFunction? It'd look like:

interface MyInterface {
  someFunction<T>(a: number): T;
}

interface MyMockedInterface extends MyInterface {
  someFunction: <T>(...params: Parameters<MyInterface['someFunction']>) => ReturnType<MockedFunction<() => T>>;
}

Notice what I'm doing is, instead of just passing MockedFunction, declaring a function type with its return type being the ReturnType of MockedFunction.