Coder Social home page Coder Social logo

Comments (19)

jpavon avatar jpavon commented on April 16, 2024 2

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.

azizhk avatar azizhk commented on April 16, 2024 1

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.

 avatar commented on April 16, 2024

I will try to check more but for a quick feedback try:

type IOneOfThem = Component1Props | Component2Props;

from react.

smashercosmo avatar smashercosmo commented on April 16, 2024

Yeah, that was the first thing that I tried. Didn't work)

from react.

 avatar commented on April 16, 2024

Will try to check this in the next two hours

from react.

tsiq-swyx avatar tsiq-swyx commented on April 16, 2024

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.

smashercosmo avatar smashercosmo commented on April 16, 2024

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.

tsiq-swyx avatar tsiq-swyx commented on April 16, 2024

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.

 avatar commented on April 16, 2024

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.

tsiq-swyx avatar tsiq-swyx commented on April 16, 2024

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.

 avatar commented on April 16, 2024

I used the generic + props merging when I used abstract React Class Components.. it's so powerful

from react.

codepunkt avatar codepunkt commented on April 16, 2024

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.

tsiq-swyx avatar tsiq-swyx commented on April 16, 2024

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.

codepunkt avatar codepunkt commented on April 16, 2024

You're welcome. I was lazy so i didn't name the types well 😆

from react.

smashercosmo avatar smashercosmo commented on April 16, 2024

@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.

swyxio avatar swyxio commented on April 16, 2024

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.

ferdaber avatar ferdaber commented on April 16, 2024

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.

swyxio avatar swyxio commented on April 16, 2024

@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.

andrewbranch avatar andrewbranch commented on April 16, 2024

(Thanks for the kind words, @azizhk 😄)

from react.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.