Skip to content

Commit

Permalink
Fix type inference bug with decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperpeulen committed Nov 15, 2022
1 parent ceca15e commit eede0fe
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 6 deletions.
20 changes: 17 additions & 3 deletions code/renderers/react/src/public-types.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, test } from '@jest/globals';

import { satisfies } from '@storybook/core-common';
import type { StoryAnnotations } from '@storybook/types';
import type { Args, StoryAnnotations, StrictArgs } from '@storybook/types';
import { expectTypeOf } from 'expect-type';
import type { KeyboardEventHandler, ReactNode } from 'react';
import React from 'react';
Expand All @@ -11,7 +11,7 @@ import type { SetOptional } from 'type-fest';
import type { Decorator, Meta, StoryObj } from './public-types';
import type { ReactRenderer } from './types';

type ReactStory<Args, RequiredArgs> = StoryAnnotations<ReactRenderer, Args, RequiredArgs>;
type ReactStory<TArgs, TRequiredArgs> = StoryAnnotations<ReactRenderer, TArgs, TRequiredArgs>;

type ButtonProps = { label: string; disabled: boolean };
const Button: (props: ButtonProps) => JSX.Element = () => <></>;
Expand Down Expand Up @@ -172,10 +172,24 @@ describe('Story args can be inferred', () => {
</>
);

// decorator is not using args
const thirdDecorator: Decorator<Args> = (Story) => (
<>
<Story />
</>
);

// decorator is not using args
const fourthDecorator: Decorator<StrictArgs> = (Story) => (
<>
<Story />
</>
);

const meta = satisfies<Meta<Props>>()({
component: Button,
args: { disabled: false },
decorators: [withDecorator, secondDecorator],
decorators: [withDecorator, secondDecorator, thirdDecorator],
});

const Basic: StoryObj<typeof meta> = {
Expand Down
13 changes: 10 additions & 3 deletions code/renderers/react/src/public-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,22 @@ export type StoryObj<TMetaOrCmpOrArgs = Args> = TMetaOrCmpOrArgs extends {
? StoryAnnotations<
ReactRenderer,
TArgs,
SetOptional<TArgs, Extract<keyof TArgs, keyof (DefaultArgs & ActionArgs<TArgs>)>>
SetOptional<TArgs, keyof TArgs & keyof (DefaultArgs & ActionArgs<TArgs>)>
>
: never
: TMetaOrCmpOrArgs extends ComponentType<any>
? StoryAnnotations<ReactRenderer, ComponentProps<TMetaOrCmpOrArgs>>
: StoryAnnotations<ReactRenderer, TMetaOrCmpOrArgs>;

type ActionArgs<RArgs> = {
[P in keyof RArgs as ((...args: any[]) => void) extends RArgs[P] ? P : never]: RArgs[P];
type ActionArgs<TArgs> = {
// This can be read as: filter TArgs on functions where we can assign a void function to that function.
// The docs addon argsEnhancers can only safely provide a default value for void functions.
// Other kind of required functions should be provided by the user.
[P in keyof TArgs as TArgs[P] extends (...args: any[]) => any
? ((...args: any[]) => void) extends TArgs[P]
? P
: never
: never]: TArgs[P];
};

/**
Expand Down

0 comments on commit eede0fe

Please sign in to comment.