[DISCLAIMER: My dev experience is quite substantial, but I just started doing TypeScript, oh... about 3 weeks ago. So if I've screwed something up...
For further actions, you may consider blocking this person and/or reporting abuse
Adam,
From this.
To this?
Here's two possible examples:
There is another way to cast Javascript into TS types. Like this:
A number of javascript folks don't like the new keyword, no problem create a factory...
Ok Adam, updated my response.
When I type:
The TS linter tells me that:
Ok make that one a class too
Or change extends to implements.
Just for reference, I'm hoping you find a way around this, or someone posts a solution. I ran into similar things and I just can't be bothered to type stuff out multiple times, it's a waste of effort for the small benefits of type safety at compile time. (I use WebStorm, it can already autocomplete everything and JSDoc will already provide me types and indication of using the wrong ones while I code). That all said, I'm a dyed in the wool C# programmer and would love to be pulling across the great parts of that to the JS world with TypeScript. But yeah, not if I'm going to spend hours of my life trying to explain to a compiler my perfectly logical structure.
Oh, yeah - I'm definitely nodding along to everything you've written here. You probably have more C# experience than me, but I've done a good bit of it and enjoy it. I also use WebStorm and find that it does a really great job of tying most things together for me - and showing me when something seems out-of-place - without using TS.
And I love the description of "trying to explain to a compiler". That really sums up some of my frustrations here. If I'm writing "bad" code, or buggy code, then of course, I'd love for any tool to be able to point that out. But it's always frustrating if you've written something that you know works perfectly well - but the compiler/linter won't stop complaining about it.
Not sure if you read to the end of the article (and I totally understand if you didn't), but for the time being, I think that last "solution" is what I'm running with for now. It's actually not too much more verbose than using
PropTypes.defaultProps
. IMHO, it's still a little "ugly" - but not so much that it makes my dev eye start twitching.I did make it to the end, but think I'd missed that you were so much closer in all of the "right!!" I was saying to the sections before haha.
Haha - I see your point.
For a class component, the correct TS syntax for default props is e.g.:
This is how you avoid having to define default values for required props -- because then of course they aren't really required, are they? In fact the React TS typedefs know how to infer that a prop provided in a defaultProp structure implies that the prop is not actually required on the component:
(FWIW I don't actually grok those typedefs, but I understand what they do.)
For a function component, if you assign defaultProps inline, TS seems to infer all the correct things, e.g.:
Hi, Craig, and thanks for the feedback. Thank you for outlining the class-based approach. I suppose I shoulda mentioned in the article that, the reason all my examples use functional components, is because the decision was made for the project that we'd be using functional React components. But I appreciate you taking the time to put those examples here.
As for the last example you give, the one that deals with functional components, there's a whole section in this article that outlines that approach - and also explains why I did not choose to use it. Primarily, there's a significant fear that
defaultProps
will be deprecated for functional components.So that's kinda what led to this whole article. There are these techniques for doing this in class-based components - but we're not going to switch everything over to class-based components over this one simple issue. Then there is this technique for doing this in a function-based component - but there's a ton of chatter that this will be deprecated - and I don't want to base the dev for a "green fields" project on something at significant risk of being deprecated. Subsequently, the search for alternate solutions becomes... ugly.
I'm with you :) I saw your post because I also think the removal of defaultProps on FCs is a mistake, and saw your link on the RFC comments
Oh, cool! I'm glad you saw my comment on the RFC.
My workaround on this weird issue
I see what you did there. By simply spreading
...defaultProps
first, those default values will be overridden by any real values that were passed in.But doesn't this leave the optional properties still defined with type
string | undefined
?? That was one of the big problems that I was trying to solve. If a prop has a default value, it should never beundefined
. And I should never have to write code that accounts for this possibility.I was wrestling with this a lot until I found this pretty sane and simple approach from typescript-cheatsheets/react:
This seems like it satisfies everything except your
defaultProps
concern, which seems like it's a really premature concern. The React 17 RC has just been released, anddefaultProps
are still here, and I'm struggling to find references to discussions about it being deprecated soon. If there were plans to deprecate it, I think we'd see something in the docs, or the usage signature would change toGreet.UNSAFE_defaultProps
in advance, as is the team's habit with things like UNSAFE_componentWillMount.FWIW, this is the open RFC thread that discusses (amongst several things) deprecating defaultProps on functional components.
github.com/reactjs/rfcs/pull/107
To be clear, it's obviously just an RFC and it may never come to pass. And I typically pay little-or-no attention to RFCs unless they become adopted and implemented. Because you can't run your programming life based on what might change in the language.
But in this case, I'd already read references to this in multiple other places. And because I was just getting into TS, I thought, "Well, if there's even a minor chance that it goes away - how do I accomplish this without the feature?" And that started my descent down the rabbit hole.
It's entirely possible that, in several years, I'll be shaking my head over the time I wasted on this, because the RFC was never adopted and defaultProps are still readily available. I hope that's the case.
Hi Adam the closest I came to a solution that could satisfy your need is following and everything is nicely typecheck
with this solution you could put the destructured props inside an args object inside your component
And typescript has a Required type that does the same as your AllPropsRequired type
hope this helps
First, thank you for showing me the Required type! I had no idea that existed. Makes a lot more sense than doing it manually with my own custom partial.
Second, I do like your approach. The only thing I find lacking in it, is the need to manually chunk those values into an
args
object (assuming you want them in a single object - like I do). But that's not really a huge objection, is it? Hmm...From looking at your example, one of the problems with my prior approaches was probably that I wasn't consistently leveraging
React.FC<>
. From many of the examples I've been looking at online, it's not at all clear that this should be used whenever creating a functional React component - but I'm starting to think that's the case.Very cool - thanks!!
Your welcome
The nice thing about the React.FC is that it defines the return type of your function and it add
children?: React.ReactNode
to your props so no need to handle that prop yourselfOIC. I think I had kinda stumbled into a different way to handle that. Cuz in my (final) example, my interface is defined as:
But I think I like the
React.FC
way better.Another "challenge" with the approach you've outlined here is that the
requiredString
andrequiredNumber
values only exist under theprops
object - butoptionalString
,optionalBoolean
, andoptionalNumber
exist as standalone variables. As you've pointed out, you can get them back into one object, but you'd have to manually addrequiredString
andrequiredNumber
to that object as well.That's not insurmountable, but I gotta play with it for a bit to see if there's a slicker way of handling that...
You could try something around these lines
whit this you would get a fully typed props object that you could use as you used to
I've actually built a helper function now that takes the existing
props
and a simple object that defines any default values and then returns it mapped to theProps
type. I'll probably outline that in a near-future post.Thanks for taking the time to point me along!
As of Nov 2022, Typescript 4.9 has a 'satifies' operator which solves this in a cleaner way, check out the stack overflow here: stackoverflow.com/questions/757665...
Check this:
Also check github.com/microsoft/TypeScript/is...
There's an entire section in my article that covers
defaultProps
. I specifically outline there why I chose to forgo that approach - because the current indication is thatdefaultProps
will be deprecated on functional components.For future readers, I think this is the best way. I wrote a short post about it dev.to/qpwo/closest-thing-to-struc...
Hi Adam.
What about the exclamation mark typescript operator?
I'm using it in combination with MyFunctionalComponent.defaultProps whenever the compiler is complaining that the prop might be null or undefined.. but I'm sure you could combine it with one of your solutions.
Honestly, I've gotta spend some more time looking into that exclamation operator. I'm new enough to TS that I'll freely admit not being aware of it. I've been travelling a bit and not able to sit down and test this in my IDE. I'm not sure that it addresses the issue - but it's definitely intriguing.
Thank you for bringing this to my attention!!
Well, well, well. I'm just starting to learn React and use Typescript for work and am doing a little research on Proptypes and I came across this article in the vast sea of articles. I didn't realize it was you. What a surprise! Thanks for this write up.
Howdy, Jannaee! Nice to see that my random angry rants are finding people in the ether. Hope all is going well with you. My sis now lives in your region. (OK, not incredibly close - but, Asheville.) Good to hear from you!
Just wanted to say this post really helped me out. Much appreciated.
Why not make it simple and readable?
vuejs.org/guide/typescript/overvie...
Hi @adam ,
I glad I'm not the only one have problems with this.
But have you tried this approach? -->
github.com/typescript-cheatsheets/...