Last active
January 17, 2019 08:36
-
-
Save kentcdodds/9d195236d182c3d11637fe3194df2bc6 to your computer and use it in GitHub Desktop.
how?
Some changes I want to point out:
size
inCommonProps
can just bekeyof typeof sizes
to only limit it to the properties defined insizes
- I changed
ButtonAsProps['as']
to satisfy your TODO, and also made it more limited to not just any string - I added overload signatures to the
Button
function
The first two are optional but the third is what should fix your issue.
Nice! Thanks! This seems to work for me! Now I just gotta figure out why it works 😅
Unsure what you were looking for for the commented out test under failing
, but typo
is allowed there because ButtonAsProps
allows any prop to be passed in, if you wanted to limit it, we'd need to convert it to a generic type to grab the allowable props for whatever is passed into as
.
Thanks. I'm pretty sure if I follow this that'll work: microsoft/TypeScript#3960 (comment)
For those who are coming in wondering how to limit the rest of the props in ButtonAsProps
, this is one solution:
import React from 'react'
declare function clsx(...args: any[]): string
const sizes = {
md: 'vx_btn--size_md',
sm: 'vx_btn--size_sm',
}
type CommonProps = {
size?: keyof typeof sizes
inverse?: boolean
secondary?: boolean
className?: string
}
type OverrideComponent = React.JSXElementConstructor<{ className?: string }> | keyof JSX.IntrinsicElements
type ButtonOnlyProps = CommonProps & {
children: React.ReactChild
} & React.ButtonHTMLAttributes<HTMLButtonElement>
type ButtonAsProps<C extends OverrideComponent> = CommonProps & React.ComponentProps<C> & {
as: C
}
function isButtonAs<C extends OverrideComponent>(props: ButtonOnlyProps | ButtonAsProps<C>): props is ButtonAsProps<C> {
return (props as ButtonAsProps<C>).as !== undefined
}
function Button(props: ButtonOnlyProps): JSX.Element
function Button<C extends OverrideComponent>(props: ButtonAsProps<C>): JSX.Element
function Button(props: ButtonOnlyProps | ButtonAsProps<any>) {
const { size, inverse, secondary, className, ...rest } = props
const finalclassName = clsx(className, 'vx_btn', sizes[size as keyof typeof sizes], {
'vx_btn--inverse': inverse,
'vx_btn--secondary': secondary,
})
if (isButtonAs(props)) {
return React.createElement(props.as, {
// TODO: See if there's a way that we can tell TypeScript that whatever
// is passed as "as" must be able to accept a "className" prop. That'd be cool
// @ts-ignore (whatever "as" is, it should expect the "className" prop)
className: finalclassName,
...rest,
})
}
return <button className={finalclassName} {...rest} />
}
export const passing = [
<Button key="k">hi</Button>,
<Button key="k" size="md">
hi
</Button>,
<Button key="k" inverse secondary>
hey
</Button>,
<Button key="k" as="a" href="#">
hi
</Button>,
<Button key="k" as="a" href="#" size="md">
hi
</Button>,
<Button key="k" as="a" href="#" inverse secondary>
hey
</Button>,
// would be sweet to be able to do
]
export const failing = [
<Button key="k" href="#">
hi
</Button>,
<Button key="k" size="blah">
hi
</Button>,
<Button key="k" inverse={2}>
hey
</Button>,
<Button key="k" secondary="true">
hey
</Button>,
// this one would be awesome if there's a way to make this work:
// <Button key="k" as="a" typo="#">hey</Button>,
]
export { Button }
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Try this:
Tested it with TS 3.2.2 and
@types/react
v16.7.20 and everything on failing is failing and everything on passing is passing.