[react] Allow to initialize state in constructor #26912
[react] Allow to initialize state in constructor #26912johnnyreilly merged 1 commit intoDefinitelyTyped:masterfrom magnetnation:patch-1
Conversation
Partial rollback of the #26813 that allow developers to follow react recommended way of initializing state in constructor
|
you didn't read what I've writen did you ? :) Please try again #26813 (comment) TL;DR: This change is no rollback at all :) If you wanna rollback, you need to remove union with null as well. |
|
You should not be rude - it will not help. |
| // In the future, if we can define its call signature conditionally | ||
| // on the existence of `children` in `P`, then we should remove this. | ||
| readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>; | ||
| readonly state: null | Readonly<S>; |
There was a problem hiding this comment.
this is not a rollback, you need to remove null from the definition as well
otherwise there will be still errors:
this will still give you might be null errors when you're gonna read the state, which is again, idiomatic typescript - properties need to be defined on class if you gonna use them in advance, or to be more precise -> TS is using state definition from base class as it was not overridden in my component
|
I'm not rude mister :) just annoyed to repeat again things that I've explicitly described and pointed to. Please remove the null as explained. And you're speaking against yourself, that null is a valid change, it's not as it will introduce "breaking change" as well |
|
@magnetnation Thank you for submitting this PR! 🔔 @johnnyreilly @bbenezech @pzavolinsky @digiguru @ericanderson @morcerf @tkrotoff @DovydasNavickas @onigoetz @theruther4d @guilhermehubner @ferdaber @jrakotoharisoa - please review this PR in the next few days. Be sure to explicitly select If no reviewer appears after a week, a DefinitelyTyped maintainer will review the PR instead. |
|
@magnetnation One or more reviewers has requested changes. Please address their comments. I'll be back once they sign off or you've pushed new commits or comments. Thank you! |
|
Maybe I missing something but react state default value is null if not initialized and therefore the declaration of state should look like:
Exactly the way you did it in the original commit. So I suggest to leave it. |
|
Regarding the errors that might arise from the union with |
yeah that's right ✌️ thanks again and sorry for casting "rude" behaviour on you, that wasn't my intention at all |
|
Are there plans to push a new package with this fix? |
|
Having this null union is a giant pain in our codebase right now... |
|
@milesj feel free to submit a PR to improve things! Being able to initialise state in the constructor is a must but otherwise go forth and conquer! |
| // on the existence of `children` in `P`, then we should remove this. | ||
| readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>; | ||
| readonly state: null | Readonly<S>; | ||
| state: null | Readonly<S>; |
There was a problem hiding this comment.
I am too late in making this comment, but I don't see why removing this readonly is at all a fix. According to TypeScript rules, readonly does not affect whether a property is writable in the constructor.
The readonly should be here to guard against assigning to state outside the constructor, which is invalid according to both TypeScript and React rules.
There was a problem hiding this comment.
Also, ideally, it would be nice to be able to use conditional types to make state abstract if the type of S is not empty; this would probably fix all the problems with state being left accidentally uninitialized without adding this null to the type. The default also should be null, and not {}, but apparently that breaks some of the mapped types that depend on S.
There was a problem hiding this comment.
You are partially correct. Readonly properties are writable in the constructors of the the class that defines the property. Not in subclasses. But it is completely valid and recommended to do so in React. See microsoft/TypeScript#25134 (comment) for more details.
I would like to emphasize that union with null is the correct way to go as react state is null unless initialized. Therefore this typescript construction describes it in the best possible way.
There was a problem hiding this comment.
what @magnetnation said.
The readonly should be here to guard against assigning to state outside the constructor, which is invalid according to both TypeScript and React rules.
That wont help, when you'll define state on your class via class property, it will override Base class readonly state and will left you just with state so you need to provide the readonly explicitly ( this might be issue on TS level )
readonly state from base class ( correct errors, but wrong runtime as no state has been defined )

The default also should be
null, and not {}
yeah that's right, also props should be object not {} etc, which would introduce another breaking changes :)
|
having the null breaks react-redux connect(). please don't do this in a patch update. |
|
hmm so I double checked again today, and this won't fix introduced breaking change so we have to rollback completely. But I told you so! @magnetnation :D #26912 (comment) :( going to submit revert PR asap. sorry everyone for trouble that this caused. We should introduce these changes with BC version after one Pro tip though: |
|
I might be not so bad idea. I would like to underline one thing. The goal of typescript definitions is not to enforce best practices or make it easier to use it in other libraries. Instead it is to describe underlying react behavior as exact as possible (same idea here). Lets not forget this. And if you are going to rollback all changes - please do not forget to reintroduce |
I think we cannot do that, as it would "prevent" users to use "react documentation way" to set state from within constructor. Hmm actually it would be still possible but they would need to define the class property type - as they should anyway - idiomatic Typescript. |
|
There's also other ways to set state during the constructor phase, that isn't written in the constructor explicitly. A few contrived examples: A helper method. Or state being derived via event/subscription/etc. FinalForm works this way. Not sure if TS is smart enough to catch these. |
best practice - you should prefer extracting initializers to pure functions
that's possible, but you have to define state upfront class Foo extends React.Component {
+ state: State
constructor(props) {
super(props);
this.api = new API(this.handleState);
}
handleState(state) {
// Lots of logic
this.state = state;
}
}Anywaysgood/best practice is to always initialize state ( same like with redux pattern ) |
|
Setting the state type property requires you to define every field in the state, with a default value, which you're already doing in the helper method. You're basically duplicating your code, and increasing your file size, especially if there's a lot of fields in your state. |
|
Also, has the readonly/null changes been verified with |
What are you talking about ? you'll have one pure function
instance definitions have no effect on static properties/methods ✌️ |
|
It was in reference to the 2nd example. You can't |
…structor. Updating @types/react to 16.4.4 in light of these two Typescript issues: * DefinitelyTyped/DefinitelyTyped#26813 * DefinitelyTyped/DefinitelyTyped#26912
…structor. Updating @types/react to 16.4.4 in light of these two Typescript issues: * DefinitelyTyped/DefinitelyTyped#26813 * DefinitelyTyped/DefinitelyTyped#26912


Partial rollback of the #26813 that allow developers to follow react recommended way of initializing state in constructor