Comments (19)
This seems to work but it feels too much boilerplate for little gain, maybe there is a better way to do it. (Note that NavLinkProps contains 'href' so you need to omit it to work as you expect)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
interface LinkProps {}
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
type RouterLinkProps = Omit<NavLinkProps, 'href'>
const Link = <T extends {}>(
props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps
) => {
if ((props as RouterLinkProps).to) {
return <NavLink {...props as RouterLinkProps} />
} else {
return <a {...props as AnchorProps} />
}
}
<Link<RouterLinkProps> to="/">My link</Link> // ok
<Link<AnchorProps> href="/">My link</Link> // ok
<Link<RouterLinkProps> to="/" href="/">My link</Link> // error
from react.
There is this amazing blog by @andrewbranch on Discriminated Unions:
https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/
Can we add this somewhere in the cheat-sheet readme. (Amazing blog, hover over variables in the snippets and see the definition)
There are a few caveats, mentioned here and also in issues microsoft/TypeScript#29703 and microsoft/TypeScript#28131
from react.
I will try to check more but for a quick feedback try:
type IOneOfThem = Component1Props | Component2Props;
from react.
Yeah, that was the first thing that I tried. Didn't work)
from react.
Will try to check this in the next two hours
from react.
this works for me... does that fit your use case @smashercosmo
type Component1Props = { a1: boolean };
const Component1: React.SFC<Component1Props> = props => <div>{props.a1}</div>;
type Component2Props = { a2: boolean };
const Component2: React.SFC<Component2Props> = props => <div>{props.a2}</div>;
const Component3 = (props: {showprop1: boolean} & (Component1Props | Component2Props)) => {
// some prop from Component1Props
const {showprop1, ...rest} = props;
if (showprop1) {
return <Component1 {...rest as Component1Props} />;
} else {
return <Component2 {...rest as Component2Props} />;
}
};
from react.
Well, not exactly) Consider real-life example
import React from 'react'
import { NavLink, NavLinkProps } from 'react-router-dom'
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
const Link = (props: ???) => {
if (props.to) {
return <NavLink {...props as NavLinkProps} />;
} else {
return <a {...props as AnchorProps} />;
}
};
export default Link;
If user passes 'to' property NavLink should be rendered, if user passes 'href' then simple anchor tag should be rendered, but passing both 'to' and 'href' should result in compilation error). So at least one of two properties (to and href) is required, but passing both of them is forbidden.
P.S. Why aren't you using React.SFC in 3rd component?
from react.
cos i dont like React.SFC haha
hmm, so you want compile time error if both to
and href
are passed. this is stumping me.
from react.
I think that is related more to TypeScript than being related to TypeScript-React.
I was busy today.. but I'll invest some time in the next hours
from react.
wow. i think writing a React component with a generic type like that should definitely be included in this list, i really really like it. i wonder if we can boil this example down a bit simpler though:
// this is just a draft, i dont know what i'm really going for here yet
const Link = <T>(props: LinkProps & T) => {
return <NavLink {...props} />
}
i realize this doesnt answer OP's original question, im just trying to figure out what i can add to the cheatsheet for this general category of problem. something like a polymorphic react component.
from react.
I used the generic + props merging when I used abstract React Class Components.. it's so powerful
from react.
Does this help?
interface IComponent1Props {
foo: string
}
interface IComponent2Props {
bar: string
}
const Component1: React.SFC<IComponent1Props> = ({ foo }) => (
<div>component 1: {foo}</div>
)
const Component2: React.SFC<IComponent2Props> = ({ bar }) => (
<div>component 2: {bar}</div>
)
type Foo = { prop1: true } & IComponent1Props
type Bar = { prop2: true } & IComponent2Props
const isFoo = (test: Foo | Bar): test is Foo => {
return (test as Foo).prop1 === true
}
const isBar = (test: Foo | Bar): test is Bar => {
return (test as Bar).prop2 === true
}
const Component3: React.SFC<Foo | Bar> = (props) => {
if (isFoo(props)) {
const { prop1, ...rest } = props
return <Component1 {...rest} />
} else if (isBar(props)) {
const { prop2, ...rest } = props
return <Component2 {...rest} />
}
return null
}
from react.
this is verbose but very readable. didnt know you could do tests like that!
i am going to add a link to this discussion. this has been very helpful to me.
from react.
You're welcome. I was lazy so i didn't name the types well 😆
from react.
@codepunkt you solution can be implemented with much less code using Discriminated Unions, but still it's annoying to specify extra props to differentiate one component from another.
@jpavon solution works, but again, it's annoying to specify generic type every time
I wonder why type guards don't work
import React from 'react'
import {
Link as ReactRouterLink,
LinkProps as ReactRouterLinkProps,
} from 'react-router-dom'
type LinkProps =
| ReactRouterLinkProps
| React.AnchorHTMLAttributes<HTMLAnchorElement>
function isAnchorProps(
props: LinkProps,
): props is React.AnchorHTMLAttributes<HTMLAnchorElement> {
return (
(props as React.AnchorHTMLAttributes<HTMLAnchorElement>).href !== undefined
)
}
export function Link(props: LinkProps) {
if (isAnchorProps(props)) {
return <a {...props} />
}
return <ReactRouterLink {...props} />
}
If I then use Link component like this
<Link href="/hi" to="/hi">
Hello
</Link>
TypeScript doesn't raise any error
from react.
fyi we've broken out the conditional rendering discussion into a bunch of different sections: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#typing-a-component-that-accepts-different-props
try the One or the Other method: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#props-one-or-the-other-but-not-both
from react.
I was very late to this party, but the recommended way of doing conditional rendering is a generic function for function components (or generic constructors for class components), unfortunately that does mean you can't use the SFC annotation. Everyone's already covered what I was going to add 🙃
from react.
@azizhk yup i already put it in https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#typing-a-component-that-accepts-different-props but its a bit buried. i havent given much thought to how this section can be organized but i hope that people read every bit thoroughly and folow through on the links.
gonna consider this closed for now, open a new issue if people wanna re-ask stuff :)
from react.
(Thanks for the kind words, @azizhk 😄)
from react.
Related Issues (20)
- [Basic] useReducer: type safe dispatcher call HOT 2
- "Props: One or the Other but not Both" does not work HOT 2
- [Question] QUESTION_TITLE_HERE
- [Advanced] Wrapping/Mirroring a Component with ref forwarding
- facing errors
- not working
- Gene Piki wants to pay 0 USD to have this issue fixed
- JSX.Element is Deprecated HOT 11
- [Bug] The logo text is overflowing in small devices it should be truncate like mobile navigation. HOT 5
- Update CSS So that website can be accessible from any device of any width -- Solved HOT 8
- [Basic] Add resources to basic/recommended/talks.md HOT 3
- [Basic] Want to Fix Navbar Text Issue Without Truncating And Decreased Footer Logo Size Responsively For all Devices HOT 3
- Table of Content HOT 2
- Linked article on defaultProps docs is paywalled HOT 3
- add as a contributor? HOT 7
- [Basic] Linting page links are not working HOT 6
- CLASS METHODS AND CLASS PROPERTIES TITLES INTERCHANGED HOT 3
- [Advanced] ISSUE_TITLE_HERE
- [Advanced] Restrict useage of child component only as a child of a particular component HOT 3
- [Basic] Provide some docs on `memo` HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from react.