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

[Bug]: expect matchers from @storybook/test are unexpectedly not generic #30539

Open
uhyo opened this issue Feb 14, 2025 · 1 comment
Open

[Bug]: expect matchers from @storybook/test are unexpectedly not generic #30539

uhyo opened this issue Feb 14, 2025 · 1 comment

Comments

@uhyo
Copy link

uhyo commented Feb 14, 2025

Describe the bug

Hello, we noticed this issue while migrating from v7 to v8. Some of expect matchers from @storybook/jest were generic (accepts type arguments), but those from @storybook/test are not.

In short, we're seeing below TypeScript error:

import { expect } from "@storybook/test";

interface FooObj {
  foo: number;
}

const obj: FooObj = {
  foo: 123,
}

expect(123).toMatchObject<FooObj>(obj);
//                        ^^^^^^
// TypeScript Error: Expected 0 type arguments, but got 1.

To compare with expect from vitest:

import { expect } from "vitest";

interface FooObj {
  foo: number;
}

const obj: FooObj = {
  foo: 123,
}

expect(123).toMatchObject<FooObj>(obj); // No error!

I think it's good for @storybook/test's expect function to behave similarly to the vitest one as much as possible, hence reporting this as a bug.

At least, the current behavior is very confusing because when user uses TypeScript's Peek Definition feature on the erroring toMatchObject call, it guides user to the signature that does have a type parameter. (You can see the behavior in the TypeScript Playground linked below.)

I think the culprit for the bug is in below code from @storybook/test:

type Promisify<Fn> = Fn extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : Fn;
type PromisifyObject<O> = {
    [K in keyof O]: Promisify<O[K]>;
};

type Matchers<T> = PromisifyObject<JestAssertion<T>> & TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>>;

Promisify<Fn> removes type parameters from Fn.

Unfortunately I don't have a specific fix to suggest, reporting this anyway.

Reproduction link

https://www.typescriptlang.org/play/?module=1&ts=5.7.2#code/JYWwDg9gTgLgBAbzgUwB5mQY3gXzgMyghDgCIABAZxmgE8AjCCAawHoZlrSBuAKF+AA7DlHwBDTMjgAxJgHl6AK0S84BJgC44ggK4h6yKHxz9MEQdTgQlW2RAXKAvCrX5NcAIwAmAMwAaXhNeNAxsAApvHwBKADoaAFkxGEwACwcsGAAeOwcAPjDrRSi+VlY1corKqoqAPTr63lK4ABVaDABlTChgMHgAUSgiKC0+9AzkABM4AAY4GDapMSgAcz1kYUo-OHodeGWIeA8YoA

Reproduction steps

Open the TypeScript Playground link above and wait for a while. Then you'll see a TypeScript error.

System

System:
    OS: macOS 15.3
    CPU: (8) arm64 Apple M2
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.13.1 - ~/.volta/tools/image/node/22.13.1/bin/node
    Yarn: 1.22.19 - ~/.volta/tools/image/yarn/1.22.19/bin/yarn <----- active
    npm: 10.9.2 - ~/.volta/tools/image/node/22.13.1/bin/npm
  Browsers:
    Safari: 18.3
  npmPackages:
    @storybook/addon-a11y: ^8.5.4 => 8.5.4 
    @storybook/addon-essentials: ^8.5.4 => 8.5.4 
    @storybook/addon-interactions: ^8.5.4 => 8.5.4 
    @storybook/addon-queryparams: 7.0.1 => 7.0.1 
    @storybook/addon-webpack5-compiler-swc: ^2.0.0 => 2.0.0 
    @storybook/manager-api: ^8.5.4 => 8.5.4 
    @storybook/react: ^8.5.4 => 8.5.4 
    @storybook/react-webpack5: ^8.5.4 => 8.5.4 
    @storybook/test: ^8.5.4 => 8.5.4 
    @storybook/test-runner: ^0.21.0 => 0.21.0 
    @storybook/testing-react: ^2.0.1 => 2.0.1 
    eslint-plugin-storybook: ^0.11.2 => 0.11.2 
    msw-storybook-addon: ^1.10.0 => 1.10.0 
    storybook: ^8.5.4 => 8.5.4

Additional context

The list of expect methods affected by this issue: toEqual, toStrictEqual, toMatchObject, toContain, toContainEqual, toBeInstanceOf, toHaveProperty, toHaveBeenCalledWith, toBeCalledWith, toHaveBeenNthCalledWith, nthCalledWith, toHaveBeenLastCalledWith, lastCalledWith, toReturnWith, tohaveReturnedWith, lastReturnedWith, toHaveNthReturnedWith, nthReturnedWith

Copy link
Contributor

greptile-apps bot commented Feb 14, 2025

** Disclaimer** This information might be inaccurate, due to it being generated automatically
The issue is in the type definitions in @storybook/test. The Promisify type helper is stripping generic type parameters. To fix: 1. Update code/lib/test-storybook/src/index.ts to modify the Promisify type: ts type Promisify<Fn> = Fn extends (...args: infer A) => infer R ? Fn extends { new (...args: any[]): any } ? Fn : (...args: A) => R extends Promise<any> ? R : Promise<R> : Fn; This preserves generic type parameters while still promisifying the return type. 2. The matchers should be defined with their generic parameters preserved: ts type Matchers<T> = { [K in keyof JestAssertion<T>]: JestAssertion<T>[K] extends (...args: infer A) => infer R ? <G>(...args: A) => Promise<R> : JestAssertion<T>[K] } & TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>>;

About Greptile

This response provides a starting point for your research, not a precise solution.

Help us improve! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

Edit Issue Bot Settings · Greptile

@uhyo uhyo changed the title [Bug]: expect matchers from @storbook/test are unexpectedly not generic [Bug]: expect matchers from @storybook/test are unexpectedly not generic Feb 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant