Typescript Generic fix - reactjs

I am new to typescript and facing issue on following for loop function. "typeof NO_PLANNING_DATA | Record<string, string | number>[] | undefined" this is the type for the data which i am passing to the "updateData" function. Since "undefined" also a type for the passing data, getting type issue for "data?.forEach" section. How to make this as a generic type.
const updateData = <T extends typeof NO_PLANNING_DATA | Record<string, string | number>[] | undefined>(data: T) => {
const items: { [x: string]: number }[] = [];
data?.forEach((val: { [x: string]: number }, index: number) => {
items[index] = {};
for (const key in val) {
if (key === "on_premise" || key === "row") {
items[index][key] = val[key];
} else {
items[index][key] = 0;
}
}
});
return items;
};

Related

How can I make argument assignable to proper parameter?

I am new to typescript and I have following error bound with setFilteredColumns(obj)
Argument of type '(false | Column)[]' is not assignable to parameter of type 'SetStateAction<Column[] | undefined>'.
Type '(false | Column)[]' is not assignable to type 'Column[]'.
Type 'false | Column' is not assignable to type 'Column'.
Type 'boolean' is not assignable to type 'Column'.ts(2345)
interface ColumnsHeader {
title: string;
disp: boolean;
}
export interface Column {
col: ColumnsHeader;
}
interface IProps{
tocompare:Column[];
columns:Column[]
}
const useBuildColumns = ({tocompare, columns}:IProps) => {
const [filteredColumns, setFilteredColumns] = useState<Column[]>();
useEffect(() => {
let obj = tocompare &&
tocompare.map((k:Column, ii:number) => {
return columns[ii] && columns[ii].col.disp === true && k;
})
setFilteredColumns(obj);
}, [columns]);
return [filteredColumns];
};
How can I resolve this error?
The problem is that you use logical operators everywhere, even where it leads to a distortion of the result. In your example map function can return false.
I didn't quite understand what was going in useEffect, maybe you tried to make something like this:
useEffect(() => {
if (tocompare) {
let obj = tocompare.filter(
(k: Column, ii: number) => columns[ii] && columns[ii].col.disp
);
setFilteredColumns(obj);
}
}, [columns]);
Remember that logical operators returns a boolean value

React component props with union type, narrow to a prop to a specific one

I created two discriminated union types for the GoodsSelector component. Don't know how to narrow the type of props to a specific one.
export type GoodsBaseInfo = { goodsName: string }
export type GoodsBaseInfoWithVersion = GoodsBaseInfo & { goodsVersion: string };
export type VersionSelectTypeGoods = GoodsBaseInfo & { versions: string[] };
export type GoodsSelectTypeGoods = GoodsBaseInfo & { hideAction?: boolean };
export type GoodsSelectTypeGoodsSelector = {
selectedType?: 'goods';
value?: GoodsSelectTypeGoods[];
onChange?(value: GoodsSelectTypeGoods[]): void;
};
export type VersionSelectTypeGoodsSelector = {
selectedType?: 'version';
value?: VersionSelectTypeGoods[];
onChange?(value: VersionSelectTypeGoods[]): void;
};
export type GoodsSelectorProps = VersionSelectTypeGoodsSelector | GoodsSelectTypeGoodsSelector;
export const isGoodsSelectTypeGoods = (goods: any): goods is GoodsSelectTypeGoods =>
'hideAction' in goods && !('goodsVersion' in goods);
export const isVersionSelectTypeGoods = (goods: any): goods is VersionSelectTypeGoods => 'versions' in goods;
export function GoodsSelector({
value = [],
onChange,
selectedType = 'goods',
}: GoodsSelectorProps) {
const onGoodsDeleteButtonClick = (record: GoodsBaseInfoWithVersion | GoodsBaseInfo) => {
onChange?.(value.filter((g) => g.goodsCode !== record.goodsCode));
};
let dataSource: GoodsBaseInfoWithVersion[] = [];
if (selectedType === 'version') {
dataSource = value.flatMap((v) =>
isVersionSelectTypeGoods(v)
? v.versions.map((version) => ({ goodsCode: v.goodsCode, goodsName: v.goodsName, goodsVersion: version }))
: [],
);
}
if (selectedType === 'goods') {
dataSource = value;
}
// use dataSource, return JSX
}
value.filter() throws error:
This expression is not callable.
Each member of the union type '{ (predicate: (value: GoodsSelectTypeGoods, index: number, array: GoodsSelectTypeGoods[]) => value is S, thisArg?: any): S[]; (predicate: (value: GoodsSelectTypeGoods, index: number, array: GoodsSelectTypeGoods[]) => unknown, thisArg?: any): GoodsSelectTypeGoods[]; } | { ...; }' has signatures, but none of those signatures are compatible with each other.
How to narrow the type of value prop to a specific one in the component so that I can use it without the type guard duplicated?
export function GoodsSelector({
value = [],
onChange,
selectedType = 'goods',
}: GoodsSelectorProps) {
// narrow the value to GoodsSelectTypeGoods or VersionSelectTypeGoods type.
// ...
// now the type of value is GoodsSelectTypeGoods everywhere in the component.
}
TypeScript Playground

How to get the value of an interface property to influence the type of a parameter in a callback of the same interface

Apologies, I wasn't sure exactly how to phrase this question because I don't know enough about typescript generics. However I do have an example demonstrating my issue:
import React from 'react';
type DataType = Record<string, string | number | Date>;
const data: { a: string, b: number, c: Date }[] = [{ a: 'hello', b: 10, c: new Date() }, { a: 'bye', b: 20, c: new Date() }, { a: 'good', b: 30, c: new Date() }];
interface Bar<T extends DataType, K extends keyof T> {
name: K;
callback: (value: T[K]) => void
}
interface Foo<T extends DataType, K extends keyof T> {
data: T[]
bar: Bar<T, K>[]
}
function MyComponent<T extends DataType, K extends keyof T>({
data,
bar
}: Foo<T, K>) {
console.log(data);
console.log(bar)
return <div />
}
export function ExampleApp() {
return (
<MyComponent
data={data}
bar={[
{
name: 'a',
callback: (value) => console.log(value) // value has type string | number | Date
},
{
name: 'b',
callback: (value) => console.log(value) // value has type string | number | Date
},
{
name: 'c',
callback: (value) => console.log(value) // value has type string | number | Date
},
]}
/>
);
}
My question is, how can I get the value parameter of callback in bar to be the type of the value of the item with key <name> in data? Each value parameter currently type string | number | Date but I would like it so that it picks up the type from the items in data:
{
name: 'a',
callback: (value) => console.log(value) // value has type string
},
{
name: 'b',
callback: (value) => console.log(value) // value has type number
},
{
name: 'c',
callback: (value) => console.log(value) // value has type Date
},
Any help is greatly appreciated!
DataType is overly vague type and you cannot infer key-value type correspondence from it at all. The only thing it says that object having this type has only string keys and values only of types string, number and Date.
But even if you replace Record with a well defined type:
type Obj = { a: string, b: number, c: Date }
Bar still won't work as expected.
interface Bar<T, K extends keyof T = keyof T> {
name: K;
callback: (value: T[K]) => void
}
The issue is the K type is a union here. If we substitute K with it's concrete type value:'a' | 'b' | 'c' we get the following:
/*
type BarResult = {
name: keyof Obj; // 'a' | 'b' | 'c'
callback: (value: string | number | Date) => void;
}
*/
type BarResult = Bar<Obj>
playground link
As you can notice there is no one-to-one correspondence here. The name property can be any of the keys and the callback property shoud be a function accepting a parameter of pretty strange union type.
To make it work as intended we're going to use mapped types:
type MiddleBar<T> = {
[K in keyof T]: { name: K; callback: (value: T[K]) => void }
}
type MiddleResult = Bar<Obj>
Here we're iterating over keyof Obj values and assigning to each of the keys a | b | c corresponding object. Substitute T for the Obj and keys for each of the available values. For example for the key a inside the external curly brackets we'll get:
['a']: { name: 'a', callback: (value: Obj['a']) => void // Obj['a'] == string
Full MiddleResult type will look like:
/*
type MiddleResult = {
a: {
name: "a";
callback: (value: string) => void;
};
b: {
name: "b";
callback: (value: number) => void;
};
c: {
name: "c";
callback: (value: Date) => void;
};
}
*/
type MiddleResult = MiddleBar<Obj>
playground link
Almost there. We definitely see the types we need inside. The only step left to use lookup type to get the result union. When we use lookup by a union type we get union of values corresponding to the keys of that union. Simple example:
type A = { s: string, n: number }
type B = A['s'] // string
type C = A['n'] // number
type D = A['s' | 'n'] // = A['s'] | A['n'] = string | number
Making the final step and doing lookup by all keys of the T object (keyof T):
type Obj = { a: string, b: number, c: Date }
type Bar<T> = {
[K in keyof T]: { name: K; callback: (value: T[K]) => void }
}[keyof T]
/*
type Result = {
name: "a";
callback: (value: string) => void;
} | {
name: "b";
callback: (value: number) => void;
} | {
name: "c";
callback: (value: Date) => void;
}
*/
type Result = Bar<Obj>
playground link
Hmm I'm not sure if you can have different return types in the callback function, I think it might not be possible though someone else can correct me if I'm wrong.
One solution though is type checking after you get the value from the callback function:
const value = exampleFunction()
// value has type string | number | Date
if (typeof value === "string") {
// Now value has type string
}

Argument of type 'unknown' is not assignable to parameter of type 'string', with array.includes()

There are some similar questions but my situation is different. I am working on a text editor and this is my code:
const LIST_TYPES = ["numbered-list", "bulleted-list"];
const toggleBlock = (editor: Editor, format: string) => {
const isActive = isBlockActive(editor, format);
const isList = LIST_TYPES.includes(format);
// Wrap nodes at the specified location in the element container. If no location is specified, wrap the selection.
Transforms.unwrapNodes(editor, {
match: (n: Node) =>
LIST_TYPES.includes(
!Editor.isEditor(n) && SlateElement.isElement(n) && n.type
),
split: true,
});
const newProperties: Partial<SlateElement> = {
type: isActive ? "paragraph" : isList ? "list-item" : format,
};
Transforms.setNodes(editor, newProperties);
if (!isActive && isList) {
const block = { type: format, children: [] };
Transforms.wrapNodes(editor, block);
}
};
match() takes Node as argument. I passed a correct type. However In this expression !Editor.isEditor(n) && SlateElement.isElement(n) && n.type, when I hover over each statemnt, .isEditor(n), .isElement(n) and n.type gives me the same warning: "Argument of type 'unknown' is not assignable to parameter of type 'string'" even though I passed the correct type as argument. I dont event understand where "unknown" coming from.
this is type for unwrapNodes()
unwrapNodes(editor: import("..").Editor, options: {
at?: import("..").Path | import("..").Point | import("..").Range | undefined;
match?: ((node: import("..").Node) => boolean) | undefined;
mode?: "highest" | "lowest" | "all" | undefined;
split?: boolean | undefined;
voids?: boolean | undefined;
}): void;
I think the issue is with includes(). Somehow it does not evaluate this !Editor.isEditor(n) && SlateElement.isElement(n) && n.type. Because this works : match: (n: Node) => LIST_TYPES.includes(n.type as string)
I think i figured out just by wrapping the logical statement and assigning it as string
Transforms.unwrapNodes(editor, {
match: (n: Node) =>
LIST_TYPES.includes(
(!Editor.isEditor(n) && SlateElement.isElement(n) && n.type) as string
),
split: true,
});

Why got "This expression is not callable..." errors from typescript and react js?

I'm new of typescript. I don't know why got the error.
my data list array:
const {
data: CUData = { cu: [] as Array<CuType> },
} = useGetCUQuery();
let CUDataArray = CUData && CUData.cu? CUData.cu:null;
const ownCompany = CUDataArray !== null && CUDataArray.filter(
company => (company.isOwner && company.userType === 2)
);
const assignedCompany = CUDataArray!== null && CUDataArray.filter(
company => (!company.isOwner && company.userType === 3)
);
Error is:
> This expression is not callable.
Each member of the union type '{ <S extends CuType>(cb: (value: CuType, index: number, array: CuType[]) => value is S, thisArg?: any): S[]; (cb: (value: CuType, index: number, array: CuType[]) => unknown, thisArg?: any): CuType[]; } | { ...; }' has signatures, but none of those signatures are compatible with each other. TS2349
Typescript isn't sure if the type for the array CUDataArray is CuType[] or unknown[]. The source of this confusion is coming from the return type of your useGetCUQuery() function. If you edit your post with that function I can look at it.
const {
data: CUData = { cu: [] as Array<CuType> },
} = useGetCUQuery();
Note that the as Array<CuType> here only applies to the empty array which you are using as a default, not to the variable CUData if it comes from data.
Ultimately you want to fix the unknown return type at its source, which is the useGetCUQuery() function. But we can also apply a type to the variable that we get from it. Does this help at all?
type CUQueryVal = {
data?: {
cu?: Array<CuType>
}
}
const queried: CUQueryVal = useGetCUQuery();
const { data: CUData = { cu: [] } } = queried; // 'as' is no longer needed
Playground Link

Resources