diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 01f3841832..7f541d4185 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -17,6 +17,7 @@ // Ferdy Budhidharma // Johann Rakotoharisoa // Olivier Pascal +// Martin Hochel // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.6 @@ -281,13 +282,18 @@ declare namespace React { // tslint:disable-next-line:no-empty-interface interface Component

extends ComponentLifecycle { } class Component { + constructor(props: Readonly

); + /** + * @deprecated + * https://reactjs.org/docs/legacy-context.html + */ constructor(props: P, context?: any); // We MUST keep setState() as a unified signature because it allows proper checking of the method return type. // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257 // Also, the ` | S` allows intellisense to not be dumbisense setState( - state: ((prevState: Readonly, props: P) => (Pick | S | null)) | (Pick | S | null), + state: ((prevState: Readonly, props: Readonly

) => (Pick | S | null)) | (Pick | S | null), callback?: () => void ): void; @@ -299,9 +305,17 @@ declare namespace React { // always pass children as variadic arguments to `createElement`. // In the future, if we can define its call signature conditionally // on the existence of `children` in `P`, then we should remove this. - props: Readonly<{ children?: ReactNode }> & Readonly

; - state: Readonly; + readonly props: Readonly<{ children?: ReactNode }> & Readonly

; + readonly state: null | Readonly; + /** + * @deprecated + * https://reactjs.org/docs/legacy-context.html + */ context: any; + /** + * @deprecated + * https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs + */ refs: { [key: string]: ReactInstance }; diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 64d1b20955..3a04a0ab79 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -54,6 +54,72 @@ declare const container: Element; // // Top-Level API // -------------------------------------------------------------------------- +{ + interface State { + inputValue: string; + seconds: number; + } + /** + * This is a "pre EcmaScript class property era" style of setting state within constructor. + * This occurs also if you need to provide som logic before mounting your component. + * To mitigate this error you need to provide state property definition upfront. + */ + class SettingStateFromCtorComponent extends React.Component { + // uncomenting this fixes the error :) + // state: State; + constructor(props: Props) { + super(props); + // $ExpectError + this.state = { + inputValue: 'hello' + }; + } + render() { return null; } + } + + class BadlyInitializedState extends React.Component { + // ExpectError -> this throws error on TS 2.6 + // state = { + // secondz: 0, + // inputValuez: 'hello' + // }; + } + class BetterPropsAndStateChecksComponent extends React.Component { + render() { return null; } + componentDidMount() { + // $ExpectError + console.log(this.state.inputValue); + } + mutateState() { + // $ExpectError + this.state = { + inputValue: 'hello' + }; + + // Even if state is not set, this is allowed by React + this.setState({inputValue: 'hello'}); + this.setState((prevState, props) => { + // $ExpectError + props = {foo: 'nope'}; + // $ExpectError + props.foo = 'nope'; + + return { inputValue: prevState.inputValue + ' foo' }; + }); + } + mutateProps() { + // $ExpectError + this.props = {}; + // $ExpectError + this.props = { + key: 42, + ref: "myComponent42", + hello: "world", + foo: 42 + }; + } + } +} class ModernComponent extends React.Component implements MyComponent, React.ChildContextProvider { @@ -682,6 +748,7 @@ React.createFactory(TransitionGroup)({ component: "div" }); // The SyntheticEvent.target.value should be accessible for onChange // -------------------------------------------------------------------------- class SyntheticEventTargetValue extends React.Component<{}, { value: string }> { + state: { value: string }; constructor(props: {}) { super(props); this.state = { value: 'a' }; diff --git a/types/react/test/tsx.tsx b/types/react/test/tsx.tsx index 431cef2fa0..3105a3347b 100644 --- a/types/react/test/tsx.tsx +++ b/types/react/test/tsx.tsx @@ -141,6 +141,10 @@ class ComponentWithNewLifecycles extends React.Component) { return { baz: `${prevProps.foo}baz` }; } @@ -160,6 +164,10 @@ class PureComponentWithNewLifecycles extends React.PureComponent) { return { baz: `${prevProps.foo}baz` }; }