Redux toolkit and typescript - reactjs

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
}

Related

React - use generic to return one of several data structures

I have a Base type, with several interfaces extending it. I want a function to accept any of the extensions and return a collaborating data structure depending on which type is sent into the function.
I wrote up a little mock-up and am getting this error inside the if and else if in VS Code:
error in if:
let columns: {
name: string;
selector: (d: T) => string;
sortable: boolean;
}[]
Type '{ name: string; selector: (d: Ext1) => string; sortable: boolean; }[]' is not assignable to type '{ name: string; selector: (d: T) => string; sortable: boolean; }[]'.
Type '{ name: string; selector: (d: Ext1) => string; sortable: boolean; }' is not assignable to type '{ name: string; selector: (d: T) => string; sortable: boolean; }'.
Types of property 'selector' are incompatible.
Type '(d: Ext1) => string' is not assignable to type '(d: T) => string'.
Types of parameters 'd' and 'd' are incompatible.
Type 'T' is not assignable to type 'Ext1'.
Property 'asdf' is missing in type 'Base' but required in type 'Ext1'.
error in else if:
let columns: {
name: string;
selector: (d: T) => string;
sortable: boolean;
}[]
Type '{ name: string; selector: (d: Ext2) => string; sortable: boolean; }[]' is not assignable to type '{ name: string; selector: (d: T) => string; sortable: boolean; }[]'.
Type '{ name: string; selector: (d: Ext2) => string; sortable: boolean; }' is not assignable to type '{ name: string; selector: (d: T) => string; sortable: boolean; }'.
Types of property 'selector' are incompatible.
Type '(d: Ext2) => string' is not assignable to type '(d: T) => string'.
Types of parameters 'd' and 'd' are incompatible.
Type 'T' is not assignable to type 'Ext2'.
Property 'jkl' is missing in type 'Base' but required in type 'Ext2'
code:
export type Base = {
id: string;
};
interface Ext1 extends Base {
asdf: string;
}
interface Ext2 extends Base {
jkl: string;
}
const ext1Cols = [
{
name: "Asdf",
selector: (d: Ext1) => d.asdf,
sortable: true
}
]
const ext2Cols = [
{
name: "Jkl",
selector: (d: Ext2) => d.jkl,
sortable: true
}
]
function getColumns<T extends Base>({ data }: { data: T[] }) {
let columns: {
name: string;
selector: (d: T) => string;
sortable: boolean;
}[] = [];
const dataData = Object.keys(data[0]);
if (dataData.includes("asdf")) {
columns = ext1Cols
} else if (dataData.includes("jkl")) {
columns = ext2Cols
}
return columns;
It took a few tries but chatGPT actually got me to a solution.
export type Base = {
id: string;
};
interface Ext1 extends Base {
asdf: string;
}
interface Ext2 extends Base {
jkl: string;
}
interface ExtCommon {
name: string;
selector: (d: Base) => string;
sortable: boolean;
}
const ext1Cols: Array<{ name: string; selector: (d: Base) => string; sortable: boolean; }> = [
{
name: "Asdf",
selector: (d: Base) => (d as Ext1).asdf,
sortable: true
}
];
const ext2Cols: Array<{ name: string; selector: (d: Base) => string; sortable: boolean; }> = [
{
name: "Jkl",
selector: (d: Base) => (d as Ext2).jkl,
sortable: true
}
];
function getColumns<T extends Base>({ data }: { data: T[] }) {
let columns: ExtCommon[] = [];
const dataData = Object.keys(data[0]);
if (dataData.includes("asdf")) {
columns = ext1Cols
} else if (dataData.includes("jkl")) {
columns = ext2Cols
}
return columns;
}

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.

Getting error on updating boolean state in Redux

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;
});
},

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".

Array of records definition

I've got a component that uses these types and interfaces
import { FC } from 'react';
enum fieldTypes {
Text = 'Text',
DateTime = 'DateTime',
Boolean = 'Boolean',
Integer = 'Integer'
}
type Metadata = Record<string, { type: fieldTypes, widget?: string }>;
interface MetadataProperties {
metaType: string;
metadata: Array<Metadata>;
dispatchSetProperty: (properties: any) => void;
}
const MetadataComponent: FC<MetadataProperties> = ({ metaType, metadata, dispatchSetProperty }) => {...}
When I try to do
const metadata = [
{
description: {
type: fieldTypes.Text
},
},
{
title: {
type: fieldTypes.Text
}
}
];
<MetadataComponent metaType="language_metadata" metadata={metadata} dispatchSetProperty={dispatchSetProperty}/>
The last line is showing an error
TS2322: Type '({ description: { type: fieldTypes; }; title?: undefined; } | { title: { type: fieldTypes; }; description?: undefined; })[]' is not assignable to type 'Record<string, { type: fieldTypes; widget?: string | undefined; }>[]'.   Type '{ description: { type: fieldTypes; }; title?: undefined; } | { title: { type: fieldTypes; }; description?: undefined; }' is not assignable to type 'Record<string, { type: fieldTypes; widget?: string | undefined; }>'.     Type '{ description: { type: fieldTypes; }; title?: undefined; }' is not assignable to type 'Record<string, { type: fieldTypes; widget?: string | undefined; }>'.       Property 'title' is incompatible with index signature.         Type 'undefined' is not assignable to type '{ type: fieldTypes; widget?: string | undefined; }'.
I don't understand what's wrong. I've declared metadata as an Array of Metadata why is it erroring?
Any explanation would be awesome :)

Resources