Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active February 14, 2019 23:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ryanflorence/be2c385d714a3352389e30bd68e710f0 to your computer and use it in GitHub Desktop.
Save ryanflorence/be2c385d714a3352389e30bd68e710f0 to your computer and use it in GitHub Desktop.
// how do I write this:
const Profile: React.FunctionComponent<ProfileProps> = ({ userId, navigate }) => {
console.log(navigate);
return <div>{userId}</div>;
}
// as this:
function Profile({ userId, navigate }) {
console.log(navigate);
return <div>{userId}</div>;
}
// I tried this:
function Profile({ userId, navigate }): React.FunctionComponent<ProfileProps> {
console.log(navigate);
return <div>{userId}</div>;
}
// and this:
function Profile<React.FunctionComponent<ProfileProps>>({ userId, navigate }){
console.log(navigate);
return <div>{userId}</div>;
}
// neither work.
// Also we can't just do function Profile(props: ProfileProps) {}
// because Profile can be passed to a HOC or a "component" prop
// and w/o the React.FunctionComponent stuff it fails the type checking
// on React.ComponentType
@zhuangya
Copy link

function ProfileFn({ userId, navigate }: ProfileProps): React.FunctionComponent {
  console.log(navigate);
  return <div>{ userId }</div>;
}

like this?

@ryanflorence
Copy link
Author

Nope, I get this:

Type 'Element' is not assignable to type 'FunctionComponent<{}>'.
  Type 'Element' provides no match for the signature '(props: { children?: ReactNode; }, context?: any): ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)>) | (new (props: any) => Component<any, any, any>)>'

@aamorozov
Copy link

aamorozov commented Feb 13, 2019

Have you tried JSX.Element instead of React.FunctionComponent and keeping ProfileProps on object destructuring?

@sunify
Copy link

sunify commented Feb 13, 2019

Ugly, but should work:

type ProfileProps = {};

export default (function Profile(
  props: ProfileProps & { children?: React.ReactNode }
) {
  return <div />;
}) as React.FC<ProfileProps>;

@wgottschalk
Copy link

wgottschalk commented Feb 13, 2019

This should work. Also. It’s I’m typing this on my phone.

function Profile({ userId, navigate }: Props): React.ReactNode {
  return <div>{userId}</div>
}

@avocadowastaken
Copy link

avocadowastaken commented Feb 13, 2019

React.FunctionComponent<ProfileProps> is just a (props: ProfileProps) => null | ReactElement<any>,
so

function Profile({ userId, navigate }: ProfileProps): ReactElement<any> { // Sometimes `null | ReactElement<any>` if you want to return `null`
  console.log(navigate);
  return <div>{userId}</div>;
}

Also when you return jsx code, TypeScript automatically assumes that this is a ReactElement<any>, so this would be enough in most cases: Nevermind, missed HOCs part.

I stop using them long time ago, if nothing else works, I would do like this:

const enhancer = hoc(Profile as ComponentType<ProfileProps>)

@Pajn
Copy link

Pajn commented Feb 13, 2019

const Profile: React.SFC<ProfileProps> = ({userId, navigate}) => {
  console.log(navigate)
  return <div>{userId}</div>
}

@jonthomp
Copy link

Maybe need to use an as to force it's hand to cover both use cases, something like:

interface ProfileProps { ... }

function Profile({ userId, navigate }: ProfileProps) {
  console.log(navigate);
  return <div>{userId}</div>;
} as React.FC<ProfileProps>;

@jsfroth
Copy link

jsfroth commented Feb 13, 2019

I stumbled over this a few days ago, too. The typings come from DefinitelyTyped and require you to define a children prop. I think @sunify got it quite right, but I don't think that you need the as React.FC<ProfileProps>; part. The correct return type is React.ReactNode, but it should be infered.

type ProfileProps = {};

export default function Profile(
  props: ProfileProps & { children?: React.ReactNode }
) {
  return <div />;
}

If you do not want to allow children you can type children as undefined_

type ProfileProps = {};

export default function Profile(
  props: ProfileProps & { children?: undefined }
) {
  return <div />;
}

Looking at the types I think the children definition of DefinitelyTyped is to strict. As far as I understand them, they also disallow components with render props as children.

@soufDev
Copy link

soufDev commented Feb 13, 2019

const Profile: React.FC<ProfileProps> = ({ navigate, userId }: ProfileProps) => {
  console.log(navigate);
  return <div>{userId}</div>;
}

in my last project using Typescript with React I used this one

@artisonian
Copy link

artisonian commented Feb 13, 2019

const Profile: React.FunctionComponent<ProfileProps> = function Profile({ userId, navigate }) {
  console.log(navigate);
  return <div>{userId}</div>;
}

Also works, even though that's probably not what you want to do. From what I can tell, tsx doesn’t see to let you cast that function declaration using the prefix syntax.

@sunify
Copy link

sunify commented Feb 13, 2019

@artisonian nice solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment