Fix HandlerFunction type to support async callback props#33864
Fix HandlerFunction type to support async callback props#33864mixelburg wants to merge 2 commits intostorybookjs:nextfrom
Conversation
Change return type from void to any so action handlers can be assigned to async callback props without type errors.
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes ✨ Finishing Touches
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
code/core/src/actions/models/HandlerFunction.ts (1)
1-1:anyreturn type is the correct pragmatic fix, but consider adding a type-level regression test.
voidis not assignable toPromise<void>in TypeScript's structural type system, and neither isunknown.anyis the only standard escape hatch that makesHandlerFunctionassignable to async callback props — the rationale is sound. It is also consistent with the pre-existingany[]parameter type.The one concern is that
anyturns off return-type checking for callers that capture the return value of aHandlerFunction. In practice this is benign (action handlers are side-effect-only), but it's worth guarding against future regressions by adding a type-level test — for example a.test-d.tsfile assertingHandlerFunctionis assignable to() => Promise<void>and() => void:import { expectTypeOf } from 'expect-type'; import type { HandlerFunction } from './HandlerFunction'; // Should be assignable to async callback props (the fixed case) expectTypeOf<HandlerFunction>().toMatchTypeOf<() => Promise<void>>(); // Should remain assignable to plain void callbacks expectTypeOf<HandlerFunction>().toMatchTypeOf<() => void>();As per coding guidelines, TypeScript strict mode should be enabled across all packages —
anyusage should be minimised where possible, but there is no viable stricter alternative in this specific case.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/core/src/actions/models/HandlerFunction.ts` at line 1, Add a type-level regression test to lock in the `HandlerFunction` assignability: create a `.test-d.ts` file that imports `HandlerFunction` and uses `expectTypeOf` (or similar) to assert `HandlerFunction` is assignable to both `() => Promise<void>` and `() => void`; reference the `HandlerFunction` symbol in the assertions so future changes to `export type HandlerFunction = (...args: any[]) => any;` will fail the type test if regressions occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@code/core/src/actions/models/HandlerFunction.ts`:
- Line 1: Add a type-level regression test to lock in the `HandlerFunction`
assignability: create a `.test-d.ts` file that imports `HandlerFunction` and
uses `expectTypeOf` (or similar) to assert `HandlerFunction` is assignable to
both `() => Promise<void>` and `() => void`; reference the `HandlerFunction`
symbol in the assertions so future changes to `export type HandlerFunction =
(...args: any[]) => any;` will fail the type test if regressions occur.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
code/core/src/actions/models/HandlerFunction.test-d.ts (1)
5-8:toMatchTypeOfis deprecated — usetoExtendinstead.
toMatchTypeOfhas been deprecated sinceexpect-typev1.2.0; the replacement istoExtend.♻️ Proposed fix
-expectTypeOf<HandlerFunction>().toMatchTypeOf<() => Promise<void>>(); +expectTypeOf<HandlerFunction>().toExtend<() => Promise<void>>(); -expectTypeOf<HandlerFunction>().toMatchTypeOf<() => void>(); +expectTypeOf<HandlerFunction>().toExtend<() => void>();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/core/src/actions/models/HandlerFunction.test-d.ts` around lines 5 - 8, Replace deprecated expect-type matcher toMatchTypeOf with the new toExtend matcher for the type assertions involving HandlerFunction; specifically update the assertions that call expectTypeOf<HandlerFunction>().toMatchTypeOf<() => Promise<void>>() and expectTypeOf<HandlerFunction>().toMatchTypeOf<() => void>() to use toExtend instead, keeping the same generic type arguments and intent so the tests continue to assert assignability to both async and plain void callbacks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@code/core/src/actions/models/HandlerFunction.test-d.ts`:
- Line 1: Replace the deprecated expect-type import and matcher: change the
import of expectTypeOf in HandlerFunction.test-d.ts to come from 'vitest'
instead of 'expect-type', and update any usages of the old matcher toMatchTypeOf
to the new toExtend matcher (e.g., calls like
expectTypeOf(<type>).toMatchTypeOf(...) should become
expectTypeOf(<type>).toExtend(...)); ensure all occurrences in this test file
are updated so the file follows the same pattern as other type-test files.
---
Nitpick comments:
In `@code/core/src/actions/models/HandlerFunction.test-d.ts`:
- Around line 5-8: Replace deprecated expect-type matcher toMatchTypeOf with the
new toExtend matcher for the type assertions involving HandlerFunction;
specifically update the assertions that call
expectTypeOf<HandlerFunction>().toMatchTypeOf<() => Promise<void>>() and
expectTypeOf<HandlerFunction>().toMatchTypeOf<() => void>() to use toExtend
instead, keeping the same generic type arguments and intent so the tests
continue to assert assignability to both async and plain void callbacks.
| @@ -0,0 +1,8 @@ | |||
| import { expectTypeOf } from 'expect-type'; | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check import patterns in existing test-d files
echo "=== Checking test-d.ts files for expectTypeOf imports ==="
rg -n "from ['\"]expect-type['\"]" --type=ts -g '*.test-d.ts' | head -20
echo ""
rg -n "from ['\"]vitest['\"]" --type=ts -g '*.test-d.ts' | head -20
echo ""
echo "=== Checking for toMatchTypeOf vs toExtend usage ==="
rg -n "toMatchTypeOf|toExtend" --type=ts -g '*.test-d.ts' | head -30Repository: storybookjs/storybook
Length of output: 1565
🏁 Script executed:
#!/bin/bash
# Read the full test file to see what it's actually testing
echo "=== Full content of HandlerFunction.test-d.ts ==="
cat code/core/src/actions/models/HandlerFunction.test-d.tsRepository: storybookjs/storybook
Length of output: 466
🏁 Script executed:
#!/bin/bash
# Check package.json to see what version of expect-type is installed
echo "=== expect-type version check ==="
rg '"expect-type"' package.json | head -5
echo ""
# Also check for vitest version
rg '"vitest"' package.json | head -5Repository: storybookjs/storybook
Length of output: 99
🏁 Script executed:
#!/bin/bash
# Check expect-type version in yarn.lock or package-lock.json to see if v1.2.0+ is used
echo "=== Checking expect-type version in lock files ==="
rg -A2 '"expect-type"' yarn.lock | head -10
echo ""
# Also check for references to toExtend/toMatchTypeOf in package.json or docs
echo "=== Checking for expect-type changelog or docs references ==="
fd -e md -e txt | xargs rg -l "expect-type" | head -5Repository: storybookjs/storybook
Length of output: 178
🌐 Web query:
expect-type toMatchTypeOf deprecation toExtend v1.2.0
💡 Result:
In expect-type, .toMatchTypeOf() is deprecated in favor of: .toMatchObjectType() (for “object has at least these keys” checks) or .toExtend() (for “is-a / subtype” checks). There are no foreseeable plans to remove .toMatchTypeOf(), but you’re encouraged to migrate because .toMatchObjectType() is stricter and .toExtend() is equivalent for “extends” semantics. [1]
Migration:
-
If you used
.toMatchTypeOf<Expected>()to mean “Actual extends Expected” → use:expectTypeOf<Actual>().toExtend<Expected>()
[1]
-
If you used
.toMatchTypeOf<{ a: number }>()to allow extra object keys → use:expectTypeOf(value).toMatchObjectType<{ a: number }>()
[1]
Source
- expect-type README (“Why is
.toMatchTypeOfdeprecated?” and matcher docs). [1]
🏁 Script executed:
#!/bin/bash
# Check what Vitest recommends by looking at Vitest test files in the repo
echo "=== Sample of how Vitest type tests are structured in recent files ==="
cat code/addons/pseudo-states/src/types.test-d.ts | head -50Repository: storybookjs/storybook
Length of output: 1383
Update import source to 'vitest' and replace deprecated toMatchTypeOf with toExtend.
This file should align with the pattern used in other type-test files in the repo (e.g., code/addons/pseudo-states/src/types.test-d.ts):
- Import
expectTypeOffrom'vitest'instead of'expect-type' - Replace
toMatchTypeOfwithtoExtend(the deprecated API has been superseded in expect-type)
Suggested changes
-import { expectTypeOf } from 'expect-type';
+import { expectTypeOf } from 'vitest';
import type { HandlerFunction } from './HandlerFunction';
// Should be assignable to async callback props (the fixed case)
-expectTypeOf<HandlerFunction>().toMatchTypeOf<() => Promise<void>>();
+expectTypeOf<HandlerFunction>().toExtend<() => Promise<void>>();
// Should remain assignable to plain void callbacks
-expectTypeOf<HandlerFunction>().toMatchTypeOf<() => void>();
+expectTypeOf<HandlerFunction>().toExtend<() => void>();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@code/core/src/actions/models/HandlerFunction.test-d.ts` at line 1, Replace
the deprecated expect-type import and matcher: change the import of expectTypeOf
in HandlerFunction.test-d.ts to come from 'vitest' instead of 'expect-type', and
update any usages of the old matcher toMatchTypeOf to the new toExtend matcher
(e.g., calls like expectTypeOf(<type>).toMatchTypeOf(...) should become
expectTypeOf(<type>).toExtend(...)); ensure all occurrences in this test file
are updated so the file follows the same pattern as other type-test files.
Closes #23731
What I did
Changed the return type of
HandlerFunctionfromvoidtoanyso that action handlers can be assigned to async callback props (e.g.onSubmit: () => Promise<void>) without causing type errors.The issue is that
(...args: any[]) => voidis not assignable to(...args: any[]) => Promise<void>, but(...args: any[]) => anyis, sinceanyis compatible with all return types.Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
This is a type-only change. Verified that the existing
HandlerFunctionusages in the codebase (e.g.action()return type,ActionsMap, test stories) remain compatible.Summary by CodeRabbit