Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React TypeScript: Argument is not assignable to parameter of type 'never'

My Router has a useReducer and the initialState has the error Argument of type 'StateInterface' is not assignable to parameter of type 'never'. this only started happening when I use action.value in the reducer in globalState.tsx

(Router.tsx)

    import React, { useReducer } from 'react';
    import { BrowserRouter, Route, Switch } from 'react-router-dom';
    import { Layout } from './components/layout';
    import { Dispatch, Global, InitialState, Reducer } from './globalState';
    import { Home } from './routes/home';
    import { NotFound } from './routes/notFound';

    const Router: React.FC = () => {
      const [global, dispatch] = useReducer(Reducer, InitialState); //<==== Here is the error
      return (
        <Dispatch.Provider value={{ dispatch }}>
          <Global.Provider value={{ global }}>
            <BrowserRouter>
              <Route
                render={({ location }) => (
                  <Layout location={location}>
                    <Switch location={location}>
                      <Route exact path='/' component={Home} />
                      <Route component={NotFound} />
                    </Switch>
                  </Layout>
                )}
              />
            </BrowserRouter>
          </Global.Provider>
        </Dispatch.Provider>
      );
    };

    export { Router };

then global state looks like

(globalState.tsx)

    import { createContext } from 'react';

    interface StateInterface {
      warningMessage: string;
      warningShow: boolean;
    }

    interface ActionInterface {
      type: string;
      value: string | boolean;
    }

    const Dispatch = createContext({
      dispatch: (action: ActionInterface) => {
        //
      },
    });

    const InitialState: StateInterface = {
      warningMessage: '',
      warningShow: false,
    };

    const Global = createContext({
      global: InitialState,
    });

    const WARNING_TOGGLE = 'WARNING_TOGGLE';
    const WARNING_MESSAGE = 'WARNING_MESSAGE';

    const Reducer = (state: StateInterface, action: ActionInterface) => { // <= if I change ActionInterface to any the probem goes away, but my linter then fires of with no any allowed
      switch (action.type) {
        case 'WARNING_TOGGLE':
          return {
            ...state,
            warningShow: action.value, // <== If I remove `action.value and set this to `true` the error goes away
          };
        case 'WARNING_MESSAGE':
          return {
            ...state,
            warningMessage: action.value, // <== If I remove `action.value and set this to `some string` the error goes away
          };
        default:
          return state;
      }
    };

    export { Reducer, InitialState, Dispatch, Global, WARNING_TOGGLE, WARNING_MESSAGE };

and Home

(home.tsx)

    import React, { useContext } from 'react';
    import { Dispatch, Global, WARNING_MESSAGE, WARNING_TOGGLE} from '../globalState';

    const Home: React.FC = () => {
      const { global } = useContext(Global);
      const { dispatch } = useContext(Dispatch);

      const openWarning = () => {
        dispatch({ type: WARNING_TOGGLE, value: true});
      };

      const closeWarning = () => {
        dispatch({ type: WARNING_TOGGLE, value: false});
      };

      const warningMessageVerify = () => {
        dispatch({ type: WARNING_MESSAGE, value: 'Please verify your email'});
      };

      const warningMessageOffine = () => {
        dispatch({ type: WARNING_MESSAGE, value: 'You are now offline'});
      };
      return (
      <section>
      {global.warningShow && <div>Warning Open</div>}
      {!global.warningShow && <div>Warning Closed</div>}
      {global.warningMessage}
      <br/>
        <button onClick={openWarning}>Open Warning</button>
        <button onClick={closeWarning}>Close Warning</button>
        <button onClick={warningMessageVerify}>Verify Email</button>
        <button onClick={warningMessageOffine}>Offline</button>
      </section>
      );
    };

    export { Home };

like image 477
Bill Avatar asked Nov 24 '19 05:11

Bill


People also ask

Is not assignable to type never []' ts 2322?

ts(2322) arr[0] = 'a'; We declared an empty array and it got assigned a type of never[] . This type represents an array that will never contain any elements (will always be empty). To solve this, we have to explicitly type the empty array.

Is not assignable to type never +context?

The error "Type is not assignable to type 'never'" occurs when we declare an empty state array with the useState hook but don't type the array. To solve the error, use a generic to type the state array, e.g. const [arr, setArr] = useState<string[]>([]) . Here is an example of how the error occurs. Copied!

What is type Never in TypeScript?

Never is a new type in TypeScript that denotes values that will never be encountered. Example 1: The null can be assigned to void but null cannot be assigned to never type variables, nor can any other type be assigned including any. Javascript.

Is not assignable to type TypeScript?

The "Type 'string' is not assignable to type" TypeScript error occurs when we try to assign a value of type string to something that expects a different type, e.g. a more specific string literal type or an enum. To solve the error use a const or a type assertion.


2 Answers

Typescript is not able to determine a safe typecast from IActionInterface value: string | boolean to StateInterface warningShow: boolean, in this code - warningShow: action.value. It cannot safely presume that value will only contain a boolean.

There are several ways to suppress this, a simple one is -

warningShow: action.value as boolean

warningMessage: action.value as string

The as keyword will assert that the type of value is a boolean or string.

like image 85
hazardous Avatar answered Oct 20 '22 20:10

hazardous


it is a best practice to define types for action creators separately.

Instead of

interface ActionInterface {
   type: string;
   value: string | boolean;
}

use two interface and define the type based on that

interface IActionInterface {
    type: string;
}

interface IMessageActionInterface extends IActionInterface { 
    value: string;
}

interface IShowActionInterface extends IActionInterface {
   value: boolean;
}

type ActionTypes = IMessageActionInterface | IShowActionInterface;

In Reducer

const Reducer = (state: StateInterface, action: ActionTypes) => { }
like image 33
JSAddict Avatar answered Oct 20 '22 21:10

JSAddict