Getting error on updating boolean state in Redux - reactjs

My state data:
options: [
{
id: 1,
name: "Sale",
isTrue: false,
},
{
id: 2,
name: "New in",
isTrue: false,
},
],
And, in reducers I have:
closeOptions: (state) => {
state.options = state.options.map((item) => (item.isTrue = false));
},
But I am getting error in state.options with red underline in vscode. And, it shows below error:
Type 'boolean[]' is not assignable to type 'WritableDraft<{ id: number; name: string; isTrue: boolean; }>[]'.
Type 'boolean' is not assignable to type 'WritableDraft<{ id: number; name: string; isTrue: boolean; }>'.ts(2322)
More,
Unhandled Runtime Error
TypeError: Cannot create property 'isTrue' on boolean 'false'
Source
105 | closeOptions: (state) => {
> 106 | state.options = state.options.map((item) => (item.isTrue = false));
| ^
107 | },
How can I resolve this issue?
Whole project in TS

You're getting these errors:
Type 'boolean[]' is not assignable to type 'WritableDraft<{ id: number; name: string; isTrue: boolean; }>[]'.
Type 'boolean' is not assignable to type 'WritableDraft<{ id: number; name: string; isTrue: boolean; }>'.ts(2322)
Because when you use state.options.map((item) => (item.isTrue = false)) in closeOptions it's returning [false, false] whose type is boolean[] which is completely different from type WritableDraft<{ id: number; name: string; isTrue: boolean; }>[].
Change closeOptions function to this:
closeOptions: (state) => {
state.options?.forEach((item) => (item.isTrue = false))
}

If you are using Redux Toolkit, you can simply do:
closeOptions: (state) => {
state.options.forEach((item) => {
item.isTrue = false;
});
},

Related

Typescript UseContext React

I'm new to Typescript and react and am getting this error, on default card
I am trying to setup use context and will use it with use state or use reducer but I am having trouble wrapping my head around initial setup for this
Error:
Argument of type '{ cartitem: { id: number; name: string; description: string; price: string; }; addCart: () => void; removeCart: () => void; }' is not assignable to parameter of type 'CartContextType'.
Types of property 'cartitem' are incompatible.
Type '{ id: number; name: string; description: string; price: string; }' is missing the following properties from type 'ICart[]': length, pop, push, concat, and 29 more.ts(2345)
import React, { createContext } from 'react';
export interface ICart {
id: number;
name: string;
description: string;
price: string;
}
export type CartContextType = {
cartitem: ICart[];
addCart: (cart: ICart, totalAmount:number) => void;
removeCart: (id: number) => void;
};
const defalutCart={cartitem:{
id: 0,
name: 'string',
description: 'string',
price: 'string'},
addCart: () => {},
removeCart: () => { }
}
export const CartContext = createContext<CartContextType | null>(defalutCart);
Your variable defalutCart needs the property cartitem to be an array. With the correct spelling and changes, here is your updated code:
const defaultCart = {
cartitem: [
{
id: 0,
name: "string",
description: "string",
price: "string",
},
],
addCart: () => {},
removeCart: () => {},
};
Do you see how the object is now wrapped in an array? Please mark this as correct if it solves your problem.

How to type provideTags in redux-toolkit (many items endpoint)

Following code works well, however I dont manage to type providesTags correctly:
type Post = {
id: number,
name: string,
description: string,
}
const postsApiSlice = api.injectEndpoints({
endpoints: (builder) => ({
getPosts: builder.query<EntityState<Post>, void>({
query: ROUTES.POSTS,
transformResponse: (responseData: Post[]) => {
return adapter.setAll(initialState, responseData)
},
// Typescript error is here, at provideTags
providesTags: (result) => {
// What to do if result is undefined?
if (!result) return [{ type: POST_TAG, id: 'LIST' }]
const tags = (result.ids.length > 0) ?
// Typescript accepts type of next line if I return it
result.ids.map((id) => ({ type: POST_TAG, id }))
:
// Typescript also accepts type of next line if I return it
[{ type: POST_TAG, id: 'LIST' }]
return tags
}
}),
}),
})
Typescript error I get:
Type '(result: EntityState<Post> | undefined) => { type: string; id: EntityId; }[]' is not assignable to type 'ResultDescription<"Post", EntityState<Post>, void, FetchBaseQueryError, {} | undefined> | undefined'.
Type '(result: EntityState<Post> | undefined) => { type: string; id: EntityId; }[]' is not assignable to type 'GetResultDescriptionFn<"Post", EntityState<Post>, void, FetchBaseQueryError, {} | undefined>'.
Type '{ type: string; id: EntityId; }[]' is not assignable to type 'readonly TagDescription<"Post">[]'.
Type '{ type: string; id: EntityId; }' is not assignable to type 'TagDescription<"Post">'.
Type '{ type: string; id: EntityId; }' is not assignable to type 'FullTagDescription<"Post">'.
Types of property 'type' are incompatible.
Type 'string' is not assignable to type '"Post"'.ts(2322)
endpointDefinitions.d.ts(188, 5): The expected type comes from property 'providesTags' which is declared here on type 'Omit<EndpointDefinitionWithQuery<void, BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError, {}, {}>, EntityState<Post>> & { ...; } & { ...; } & QueryExtraOptions<...>, "type"> | Omit<...>'
If I return just result.ids.map((id) => ({ type: POST_TAG, id })) or just [{ type: POST_TAG, id: 'LIST' }] works correctly.
How can I type it?
Also, not sure what should I do when result is undefined. Should I return [{ type: POST_TAG, id: 'LIST' }]?
This is described in typing providesTag/invalidatesTags.
You will need a few as const assertions when you define POST_TAG.
So not const POST_TAG = "foo", but const POST_TAG = "foo" as const should do the trick. Otherwise it will be typed as "string", not as "foo".

Redux toolkit and typescript

I am learning TS, and currently have a problem with the redux-toolkit in it.
I want to create a simple toggle for true/false value but got an error.
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import { BooleanLiteral } from "typescript";
const initialState = {
hidden: true,
};
const menuSlice = createSlice({
initialState,
reducers: {
setHidden: (state) => (state.hidden = !state.hidden),
},
});
error : ype '(state: WritableDraft<{ hidden: boolean; }>) => boolean' is not assignable to type 'CaseReducer<{ hidden: boolean; }, { payload: any; type: string; }> | CaseReducerWithPrepare<{ hidden: boolean; }, PayloadAction<any, string, any, any>>'.
Type '(state: WritableDraft<{ hidden: boolean; }>) => boolean' is not assignable to type 'CaseReducer<{ hidden: boolean; }, { payload: any; type: string; }>'.
Type 'boolean' is not assignable to type 'void | { hidden: boolean; } | WritableDraft<{ hidden: boolean; }>'.
The reason is that you are trying to return statement, you just need to change braces like this
setHidden: state => {
state.hidden = !state.hidden
}

Nested Object -> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type

I have a react form component that I'm trying to rewrite to typescript. I am trying to retrieve an object within another object in a for loop but I keep getting the following error
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
I've attempted the solution presented in the following questions but I was still unable to have a working solution and I do not wish to set noImplicitAny to false in tsconfig.json:
TypeScript: Object.keys return string[]
TypeScript TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
element implicitly has an 'any' type because type '{0}' has no index signature.
The code in question is - The problematic area can be found near the bottom:
import React from 'react';
import Button from '../../UI/Button/Button';
import Input from '../../UI/Input/Input';
type Config = {
elementType: string;
elementConfig: {
type: string;
placeholder: string;
options: {
value: string;
displayValue: string;
};
};
value: string;
validation: {
required: boolean;
isEmail?: boolean;
minLength?: number;
};
valid: boolean;
touched: boolean;
fieldActive: boolean;
// [key: string]: string | Object;
}
interface SignInFormProps {
isSignIn: boolean;
}
interface SignInFormState {
controls: {
email: Config;
password: Config;
};
isSignUp: boolean;
[key: string]: boolean | Object | Config;
}
class SignInForm extends React.Component<SignInFormProps, SignInFormState> {
state = {
controls: {
email: {
elementType: 'input',
elementConfig: {
type: 'email',
placeholder: 'Your Email',
options: {
value: '',
displayValue: ''
},
},
value: '',
validation: {
required: true,
isEmail: true
},
valid: false,
touched: false,
fieldActive: false
},
password: {
elementType: 'input',
elementConfig: {
type: 'password',
placeholder: 'Password',
options: {
value: '',
displayValue: ''
},
},
value: '',
validation: {
required: true,
minLength: 6
},
valid: false,
touched: false,
fieldActive: false
}
},
isSignUp: true
}
private activateField = ( controlName: keyof SignInFormState['controls'] ) => {
do stuff...
}
...
render() {
const formElementsArray: {id: string, config: Config}[] = [];
// ################ The config value is causing the error ################
for ( let key in this.state.controls ) {
formElementsArray.push({
id: key,
config: this.state.controls[key] as Config
});
}
let form = formElementsArray.map( formElement => (
<Input
blur={ ( event ) => this.disableFocus(event, formElement.id) }
changed={ ( event ) => this.inputChangedHandler(event, formElement.id) }
elementConfig={formElement.config.elementConfig}
elementType={formElement.config.elementType}
fieldActive={formElement.config.fieldActive}
focus={ () => this.activateField(formElement.id) }
invalid={!formElement.config.valid}
key={formElement.id}
shouldValidate={formElement.config.validation.required}
touched={formElement.config.touched}
value={formElement.config.value} />
));
If anybody has any ideas on how to resolve this while having clearly defined types and without using any then that would be helpful.
First of all, there isn't a nice way to do this and it's still in discussion: https://github.com/Microsoft/TypeScript/issues/3500
Below are 2 potential ways to solve your issue.
Declare the key outside of the loop:
const formElementsArray: { id: string; config: Config }[] = [];
let key: keyof typeof this.state.controls;
for (key in this.state.controls) {
formElementsArray.push({
id: key,
config: this.state.controls[key],
});
}
Use Object.keys & cast the key to the desired type:
const formElementsArray: { id: string; config: Config }[] = (Object.keys(
this.state.controls
) as (keyof typeof this.state.controls)[]).map((key) => ({
id: key,
config: this.state.controls[key],
}));
You could also make it clearer by using an enum for the control key
enum ControlKey {
email = "email",
password = "password",
}
interface SignInFormState {
controls: {
[key in ControlKey]: Config;
};
isSignUp: boolean;
[key: string]: boolean | Object | Config;
}
then
1b.
const formElementsArray: { id: string; config: Config }[] = [];
let key: ControlKey;
for (key in this.state.controls) {
formElementsArray.push({
id: key,
config: this.state.controls[key],
});
}
or
2b.
const formElementsArray: { id: string; config: Config }[] = (Object.keys(
this.state.controls
) as ControlKey[]).map((key) => ({
id: key,
config: this.state.controls[key],
}));
Hope this helps

Can't set columns for react data grid. Gives non assignable type error

I am new to both React and TypeScript and am trying to make a simple React Data Grid based on their simple example here: https://adazzle.github.io/react-data-grid/docs/quick-start
Here is what I have:
import React from 'react';
import ReactDataGrid from 'react-data-grid';
const columns = [
{ key: "id", name: "ID", editable: true },
{ key: "title", name: "Title", editable: true },
{ key: "complete", name: "Complete", editable: true }
];
const rows = [
{ id: 0, title: "Task 1", complete: 20 },
{ id: 1, title: "Task 2", complete: 40 },
{ id: 2, title: "Task 3", complete: 60 }
];
export const DataTable: React.FC = () => {
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
/>
);
};
However I keep getting an error on the link where it sets the columns in the table. It says:
(JSX attribute) DataGridProps<{ [x: string]: {}; }, React.ReactText>.columns: (ColumnValue<{
[x: string]: {};
}, unknown, string> | ColumnValue<{
[x: string]: {};
}, unknown, number>)[]
Type '{ key: string; name: string; editable: boolean; }[]' is not assignable to type '(ColumnValue<{ id: number; title: string; complete: number; }, unknown, "id"> | ColumnValue<{ id: number; title: string; complete: number; }, unknown, "title"> | ColumnValue<{ id: number; title: string; complete: number; }, unknown, "complete">)[]'.
Type '{ key: string; name: string; editable: boolean; }' is not assignable to type 'ColumnValue<{ id: number; title: string; complete: number; }, unknown, "id"> | ColumnValue<{ id: number; title: string; complete: number; }, unknown, "title"> | ColumnValue<{ id: number; title: string; complete: number; }, unknown, "complete">'.
Type '{ key: string; name: string; editable: boolean; }' is not assignable to type 'ColumnValue<{ id: number; title: string; complete: number; }, unknown, "complete">'.
Types of property 'key' are incompatible.
Type 'string' is not assignable to type '"complete"'.ts(2322)
DataGrid.d.ts(6, 5): The expected type comes from property 'columns' which is declared here on type 'IntrinsicAttributes & DataGridProps<{ id: number; title: string; complete: number; }, "id" | "title" | "complete"> & { ref?: ((instance: DataGridHandle | null) => void) | RefObject<...> | null | undefined; }'
Can anyone help me parse this error? Do I need to explicitly set the type of the columns to the type that the react data grid is expecting?
I installed the react-data-grid types and the error went away:
npm install --save #types/react-data-grid

Resources