A few days ago, I posted a long article about my struggle to find a solution for setting default prop values in React/TS components. Based on feed...
For further actions, you may consider blocking this person and/or reporting abuse
Hi Adam,
It's my first time commenting here but I came across the same problem last week and I wanted to share my solution with you.
I certainly appreciate your input. And if this solution works for you, then... great! But I have to say that, for me, there is one key aspect that basically falls short. It's in this:
Again, if that works for you, then... awesome! But I personally think this makes no sense. And it is part of the frustration I had in working through this problem.
If I have a prop - let's say it's
X
- and that prop is listed as an optional prop, of type string, then, with no further information, I understand why we need to validate whetherX
is of typestring
before we perform a.split()
on it. This makes sense because theX
variable could be astring
OR it could beundefined
.But if that optional prop also has a default value set, then it makes no sense that I have to validate its data type before doing a
.split()
. Because the variable can never beundefined
! If a value was provided in the prop, then TS is supposed to be ensuring us that this value was astring
(and can thus be.split()
). And if a value was not provided in the prop, then we know that the default value will be used - which is astring
- and can be.split()
.This is the heart of my (massive) frustration with this shortcoming in TS. If I've defined
X
as typestring
and I've provided a defaultstring
value, then I should never again have to validate that the prop value is, in fact, astring
.It will ALWAYS be a
string
. And it will NEVER beundefined
.Yeah I just understood that my
DEFAULT_PROPS
will not act as fallback values. My bad :( ! I did more research after thinking about that. What do you think of Typescript official solution to the problem ?typescriptlang.org/docs/handbook/r...
I have several problems with their "official solution".
First, as stated in my previous rebuttals, this approach still wipes out the
props
namespace. In fact, look at their example. I think it kinda makes my case for me.How many times have you had some function inside your component where there's a variable called something generic - like "name"?? And that's fine - but what if you also had a prop that was passed in called "name". Now you run into naming collisions. Because "name" is defined at the component level. So if you try to set something else as "name", you end up overwriting your props.
But if you have
props.name
, then you can always set some other variable to "name", secure in the knowledge that the props will always live underprops.name
.The "official solution" also falls down if you want to use anything more than simple types. For example, what if you want to use a union type???
Imagine that "name" can be a string (like "Adam Davis") or an object (like, for example:
{firstName: 'Adam', lastName: 'Davis'}
. But there's no way to annotate that in the example above. You're stuck just using inference to determine the data type - and if you're gonna do that, might as well stick with JS.Also, I'll note that, for me at least, I don't "like" these kinds of solutions because (as I outlined in some parts of my article), I really appreciate the fact that, in "old skool" React/JS, the props are all inside the
props
object. I realize now, after talking to other "TS-types", that some people really don't care much about this. But for me, it's very important.I really value the ability to simply read through code and know, on first read, that this value comes from props and this other value comes from some where else. And how do I know that just by reading a line-or-two of code? I know it because the prop values have
props.xxxx
right in their name.Granted, in my "final solution", the props are shunted into another object - which I've called
args
. But the difference is semantic. The important point is that my props remain in an object namespace that clearly defines them as coming from the props.Unless I'm missing something, this can be simplified considerably if you're willing introduce a second variable inside your component to hold the props-with-defaults-applied. Have you considered something like:
?
Hmm... When I was going through this headache in June, I tried about 1,000 different things. But I don't think I specifically tried this. I do like leveraging the multiple-spread-operator approach to first set default values - and then overwrite them if they were provided in
propArgs
. I do this when I'm settingstyle
attributes with CSS-in-JS.I'm not seeing any downside to this approach at the moment. And it doesn't require any helper function/Hook.
Awesome!
Hi,
TypeScript should make work easier, not harder. Although I find your article really interesting, your solution is really much too complex for me. Seems like React and Typescript don't really belong together. The best solution I have found so far is this one : /* eslint-disable react/require-default-props */. Short and painless.
The best solution I've found so far isn't even the one outlined in this article. It's the one suggested by Tim Lieberman just above in the comments. However, the fact that it took me two articles to find that solution, and the fact that it's nowhere-near intuitive for someone who's just trying to switch from React/JS to React/TS, annoys me about how some things that are dead simple in React/JS can become incredibly convoluted in React/TS.
But still, you showed me what's possible, thank you.
Hi Adam,
You may want to try this :
it's shorter, delegates type checking of optionals to the type
Optionals<T>
. I suggest you try the code to understand it. it needs typescript 3.8 (worth upgrading for?.
and??
operators ).Thank you! Since I'm new to the TS stuff, your solution looks a bit gobbledy-gookish to me at first, so I'll definitely look at it carefully to make sure that I truly grok it before putting it in. But this looks very promising!
And we are already using TS 3.8.3 - so
?
and??
operators shouldn't be a problem.Cheers!
Yes, the hardest part is this one :
Witch means, return me an object type that has the same properties as Props, but with property types equal to the property name if the underlying property is not required .
then :
that means return me a union of all the optional properties names of Props.
Then the idea is to have setDefaults(), not compile if you try to pass default parameters not declared in Props, or if you forgot to add defaults that where declared optional in Props.
This is where you will thank typescript for having your back everytime you forgot to handle a default parameter, because typescript will catch it.
Excellent stuff. And thank you(!) for the extended explanation. I've been coding for 20+ years and I've done plenty of generics in languages like C#. But when you're in a "JavaScript mindset", it can still take a little bit to get your head back in that mindspace.
All though I'm generally a big fan of generics, when you haven't been working with them for awhile, code like that above can look like the first time that you dove into regular expressions.
Cheers!
What's wrong with Component.defaultProps = {}??
Or you are just digging into rabbit hole for educational purposes?
From the article above, Requirement #7:
I also discussed the presumed deprecation plans in the previous article.
Ohh I see, okay this makes sense. :D