diff --git a/.storybook/src/v4.0.0.mdx b/.storybook/src/v4.0.0.mdx index e7f473751..a6fd32dee 100644 --- a/.storybook/src/v4.0.0.mdx +++ b/.storybook/src/v4.0.0.mdx @@ -121,6 +121,25 @@ After ``` +### ButtonのGeneric propsについて + +[Styled Componentの制限](https://github.com/styled-components/styled-components/issues/1803)により`styled(Button)`で拡張する場合propsがうまく推論されない場合があります。 +Charcoal v4.1.0から過剰なpropsを検出する処理を追加したが、`component` propと合わせて使う時の型を配慮する必要があります。 + +サンプル + +```tsx +// allow component +const StyledButton = styled(Button)`` as typeof Button + + +// preserve component type +const StyledButtonA = styled(Button<'a'>)`` + +``` + +その他のケースは [styledButtonTypeTest.d.tsx](https://github.com/pixiv/charcoal/blob/main/packages/react/src/components/Button/styledButtonTypeTest.d.tsx) を参考してください。 + ## Checkbox - `Checkbox`の`input`要素を`children`がない場合に label で囲わないようにしました。 diff --git a/packages/react/src/components/Button/index.tsx b/packages/react/src/components/Button/index.tsx index d36be7078..677cb04f9 100644 --- a/packages/react/src/components/Button/index.tsx +++ b/packages/react/src/components/Button/index.tsx @@ -46,5 +46,11 @@ const Button = forwardRef(function Button( ref={ref} /> ) -}) as (p: ButtonProps) => JSX.Element +}) as ( + p: 'button' extends T + ? ButtonProps<'button'> + : ButtonProps & { + component: T // required + } +) => JSX.Element export default Button diff --git a/packages/react/src/components/Button/styledButtonTypeTest.d.tsx b/packages/react/src/components/Button/styledButtonTypeTest.d.tsx new file mode 100644 index 000000000..3680ae693 --- /dev/null +++ b/packages/react/src/components/Button/styledButtonTypeTest.d.tsx @@ -0,0 +1,81 @@ +// This file is for type testing only + +// only use the type +import type { default as ButtonType } from './index' +import type styledType from 'styled-components' + +declare const Button: typeof ButtonType +declare const styled: typeof styledType + +const Custom = ({ custom }: { custom: string }) => <>{custom} +const CustomGeneric = ({ custom }: { custom: C }) => ( + <>{custom} +) + +const StyledButton = styled(Button)`` +const StyledButtonAsButton = styled(Button<'button'>)`` +const StyledButtonA = styled(Button<'a'>)`` +const StyledButtonCustom = styled(Button)`` +const StyledButtonCustomAsButton = styled( + Button +)`` as typeof Button +const StyledButtonCustomGeneric = styled(Button)`` +const StyledButtonCustomGenericFoo = styled( + Button> +)`` + +// for type test only +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function Tests() { + return ( + <> + {/* OK */} + + + + + + + component="a" href="#" /> + > + component={CustomGeneric} + custom="bar" + /> + + + + } + custom="foo" + /> + + {/* NG */} + + {/* @ts-expect-error Property 'href' does not exist on type */} + + {/* @ts-expect-error Property 'href' does not exist on type */} + + {/* @ts-expect-error Property 'component' is missing */} + + {/* @ts-expect-error Property 'disabled' does not exist on type */} + + {/* @ts-expect-error Type '"button"' is not assignable to type '"a"' */} + + {/* @ts-expect-error Property 'component' is missing */} + + {/* @ts-expect-error Type 'string' is not assignable */} + + {/* @ts-expect-error Property 'custom' does not exist on type */} + + {/* @ts-expect-error Type 'href' is not assignable */} + href="#" /> + } + custom="foo" + /> + {/* @ts-expect-error '""' is not assignable to type '"foo"' */} + + + ) +}