15

I'd like to take advantage of the static and strong typing in TypeScript, but only for the state since I don't intend to take in any props.

When I attempt to pass the interface as such, I end up with an error:

import * as React from 'react';
import {Link} from 'react-router-dom';
import Constants from '../Constants';

interface ILoginState {
   email: string;
   password: string;
   remember: boolean;
   error: string;
   isLoading: boolean;
}

class LoginView extends React.Component<{}, ILoginState> {

   constructor(props) {
      super(props);

      this.state = {
         email: '',
         password: '',
         remember: false,
         error: '',
         isLoading: false
      };
   }

 render() {
      return (<div>Login goes here</div>
      );
   }
}

export default LoginView;

I end up with a compile error:

ERROR in [at-loader] ./src/scripts/pages/LoginView.tsx:41:21 
    TS2345: Argument of type '{ [x: number]: any; }' is not assignable to parameter of type 'ILoginState | ((prevState: Readonly<ILoginState>, props: {}) => ILoginState | Pick<ILoginState, "...'.
  Type '{ [x: number]: any; }' is not assignable to type 'Pick<ILoginState, "email" | "password" | "remember" | "error" | "isLoading">'.
    Property 'email' is missing in type '{ [x: number]: any; }'.

I've also tried using 'any' in place of the empty brackets but that doesn't work either.

Here's the line 41 (this.SetState...) that the trace is referring to:

   handleChange = event => {
      const target = event.target;
      const value = target.type === 'checkbox' ? target.checked : target.value;
      this.setState({
         [target.name]: value
      });
   }

Here are all examples where that's used:

<input name="email" type="email" value={this.state.email} onChange={this.handleChange} />
<input name="password" type="password" value={this.state.password} onChange={this.handleChange} />
<input name="remember" type="checkbox" checked={this.state.remember} onChange={this.handleChange} />
3
  • The error indicates the problem is with the type of the state, not the props. Which line of your snippet is line 41? (can you just add a comment on that line so we know where the error is)? Moreover, from the error, it looks like the problem is with a setState call. Do you have a setState call in that component that you left out of the example? If so, can you include it? Commented Jun 5, 2018 at 0:06
  • @CRice Added the requested snippet Commented Jun 5, 2018 at 0:10
  • 1
    Looks like event.target.name is inferred to have type number (not sure why), and target.value type any. Since the name inferred as number, I don't think you can cast it to be keyof ILoginState (which would have been the best option). This may be one of those cases where the easiest thing is just to cast the state object to any. EG: this.setState({[target.name]: value} as any), since I'm not sure where that number inference is coming from, or what you'd have to change to correct it. Hopefully someone who knows more about react can give a better answer. Commented Jun 5, 2018 at 1:03

2 Answers 2

12

Option 1

class LoginView extends React.Component<{}, ILoginState> {
   constructor({}) {
   }
}

Option 2

class LoginView extends React.Component<null, ILoginState> {
   constructor(null) {
   }
}

Since props default value is an empty object, I personally prefer option 1.

Edit: it appears latest typescript versions do not support option 1. I'll suggest one of the following methods (tested on TS 3.7):

class LoginView extends React.Component<{}, ILoginState> {
   constructor() {
       super({});
   }
}

// OR
class LoginView extends React.Component<{}, ILoginState> {
   constructor(props = {}) {
       super(props);
   }
}

Thanks to @Andy who brought this update to my attention.

Sign up to request clarification or add additional context in comments.

3 Comments

I find option 1 gives me an error when I try to instantiate the component in jsx like <LoginView />. I get error TS2322: Type '{}' is not assignable to type 'never'
Nether of these work in 2022 for the defalt typescript/recommended rules Don't use `{}` as a type. `{}` actually means "any non-nullish value". - If you want a type meaning "any object", you probably want `Record<string, unknown>` instead. - If you want a type meaning "any value", you probably want `unknown` instead. - If you want a type meaning "empty object", you probably want `Record<string, never>` instead.eslint@typescript-eslint/ban-types
@gman This actually works perfectly on TS 4.8.4 (using create-react-app). If you've added an extra ESlint rule, you can change it accordingly. see stackoverflow.com/a/66773898/7126139
0

If you do not have props you can just do that :

export interface IMyClassState {
  menuItems: MenuItem[];
}

export default class MyClass extends Component <Record<string, never>, IMyClassState>{
  constructor(props = {}) {
      super(props);
      this.state = {
        menuItems: []
      } as IMyClassState;
    }
    
...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.