typescript-cheatsheets / react

Cheatsheets for experienced React developers getting started with TypeScript

Home Page:https://react-typescript-cheatsheet.netlify.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Advanced] Props: Pass One ONLY IF the Other Is Passed - memoized arrow function

RemyMachado opened this issue · comments

What cheatsheet is this about? (if applicable)
Props: Pass One ONLY IF the Other Is Passed

Advanced cheatsheet

What's your issue or idea?

I'm struggling to make it work with an arrow function component using React.memo.

here's the component declaration:

type CommonProps = { foo: string }
type Props1 = CommonProps & { bool: true }
type Props2 = CommonProps & { bool: false, additionalProp: number }

const MyComponent: React.FC<Props1 | Props2> = React.memo(({ foo, bool, additionalProp /* error here */ }) => {
    ...
})

I'm getting the following error for Props2 that defines additional props:

TS2339: Property 'additionalProp' does not exist on type 'PropsWithChildren >'.

You can define the type for the additionalProp as neverin the case of bool === true.
So the resulting Props1 would look like this: type Props1 = CommonProps & { bool: true, additionalProp: never }

The result is that if bool is true then the additionalProp cannot be set otherwise you get a TypeScript error.
Within MyComponentyou'll have to check whether bool is true or false and then additionalPropshould have the correct type.

I've created an example here: https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg9gWwXAdgBQE5zAZygXigG8oAzOOALih2AwEsUBzKAXwChRIpNscBGArETJ0WXFABkxKACMKAG2p0ArhAA0UAIYATHfWD1UWhbzDUUEAG4QMbTuGhmcAJiHwkqZ1Jny4SshMcDW09AyMUEzMLFQRZW3sHbgAlCBwVBUNmb0JnQQAfHnFXAG52dlIVFABjQ1QoYDTgAAoicjhNPwVNXX06yNNxVmpU9MzGJmcASmJ2KHmoelIoZq6CfEJVCBmiOYX96tRaUL6IkyFe8ONB7BKoAHp7qAALLTwuaEsbDD351igIApgrN9gcjsATlcBhcwv0ouI7o8Xm8Go4oChYvEfvsOBxOE1Wr8yBRqABydqk9RErrUUhBDREy5wm7mKD8disKblJEAIQoCAAhPjaIT9u0yRSqfsaQ0MGopQsmWcWdR2Zz2EA

I'm not sure how I'm supposed to use the variant. If I'm omitting the additionalProp in your playground:

test({
    foo: 'foo',
    bool: true
})

I'm getting:
Property 'additionalProp' is missing in type '{ foo: string; bool: true; }' but required in type '{ bool: true; additionalProp: never; }'.

Sorry, what should work is type Props1 = CommonProps & { bool: true, additionalProp?: never } (adding the question mark to the definition of additionalProp)

Here's another link to the updated example: https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg9gWwXAdgBQE5zAZygXigG8oAzOOALih2AwEsUBzKAXwChRIpNscBGArETJ0WXFABkxKACMKAG2p0ArhAA0UAIYATHfWD1UWhbzAB+aiggA3CBjadw0MzgBMQ+ElSupM+XBKZCY4Gtp6BkYoJmZWKgiy9o5O3ABKEDgqCobMvoSuggA+POLuANzs7KQqKADGhqhQwBnAABRE5HCaAQqauvoN0abirNTpmdmMTK4AlMTsUItQ9KRQrT0E+ISqEHNEC0uHtai04QNRJkL9kcbD2GVQAPSPUAAWWnhc0DU6EKSMEB0B0WrCgEAUoXmhyOJ2AZxuQyuEUGMXED2ebw+TWcUBQ8USGGBjg4nBa7SJnWoAHJOlT1ESetRSCENETrii7mBqPx2KwZpVmrRyYdKVAaRQ6QzFMoMGpefzKhiAEIUBAAQlJQv2Ioo1Np9MOjKastZh3ZF053Pl7CAA

But I just saw that this yields a weird error message when trying to pass additionalProp along with a bool value of true.

Another solution might be using unions of two conditional types but you'll have quite some generics in there (in short in the function test we expect an object that has a bool property of type boolean. Then we forward this to our ResultingProps type and TypeScript checks with the conditionals which type is it by checking if our bool is true or false): https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg9gWwXAdgBQE5zAZygXigG8oAzOOALih2AwEsUBzKAXwChRIpNscBGArETJ0WXFABkxKACMKAG2p0ArhAA0UAIYATHfWD1UWhbzAB+aiggA3CBjadw0MzgBMQ+ElSupM+XBKZCY4Gtp6BkYoJmZWKgiy9o7sAPQpXNAAShA4KgqGzL6EroIAPjzi7gDcTtzZufmMTK4APAAqAHxCABRtUBAAHsAQKDp4RAFBqtCsUOYVfIJWtvYAlFDlvf1DI2PEk9SkITNzC7gey3YYqzXspCooAMaGqFDDtO3bw6PjB3KKEC0KFYHW6RHIcE0k00un0L2ipnErGo9TyBWalXaHXWRHYUHxUHopCg3UmBHwhGmOLxBNpj1QtHCcKiJiEsMixkR2CqUDSUAAFlo8BkoA8dBBSIwIDoafjZhAFKFiLLaVB6ShGez4azCFqWVywDy+YLhc4oCh4okMCqOBxODlgC1XG5QbjaRDqAByCGe9Qqv5HRUaFV6zmxKD8disVbse0fEqulUeqDeii+-2KZQYNRRmOxvkAIQoCAAhHHHQmwUmKF6fX7aX9pvWCaGEeHI9H2EA

To check how to use generics in combination with JSX, please refer to https://mariusschulz.com/blog/passing-generics-to-jsx-elements-in-typescript

Yet another solution (and probably the easiest and least fancy) would be to not try to destructure the props in the function signature but instead receive it as the object props and check with a type-guard if it's the one or the other type. I haven't tested it but this simpler solution might be the best :-)

Making the additionalProp an optional never is working on my side. Which error did you encounter?


Another solution might be using unions of two conditional types but you'll have quite some generics

Yes, it's not ideal.

receive it as the object props and check with a type-guard

Yes, it would work, but I really wanted to learn a way to do it with destructuration.


Thank you for these solutions!

thanks Remy!