When I hover over ...res in VSCode I get the warning from my linter:
Spread types may only be created from object types
When I log res it's either a string or an object. However, I have no idea how to satisfy the linter in this case.
function getCookie(req: Request, key: string): string | undefined {
const {
headers: { cookie },
} = req;
return (
cookie &&
cookie.split(";").reduce<string | undefined>((res, item) => {
const data = item.trim().split("=");
return <string | undefined>{ ...res, [data[0]]: data[1] };
}, "")
);
}
So let's deal with the JavaScript and TypeScript aspects separately. In plain JavaScript terms, your function doesn't do what you've described. When there are cookies defined, it returns a Record<string, string> mapping cookie names to cookie values, not a particular string or false. If you mean for it to do something with key, you haven't included that part in your example.
When no cookies are defined, it ought to return undefined, but because you test the truthiness cookies in your return statement, it could return an empty string instead. That can be fixed by making that test separately and returning undefined explicitly. (Make sure your linter is warning you about using non-booleans in boolean context in TS.)
The reason you're spreading a string is that you're passing an empty string "" as the initial value to Array.prototype.reduce. Since you are accumulating objects, not strings, your initial value should be {} (an empty object) instead.
That helps us see the TypeScript issues. Your string | undefined casts are all unnecessary once the initial value is corrected, and the return type of the function overall becomes Record<string, string> | undefined. In sum:
function getCookie(req: Request, key: string): Record<string, string> | undefined {
const {
headers: { cookie },
} = req;
if (!cookie) {
return undefined;
}
return cookie.split(";").reduce<Record<string, string>>((res, item) => {
const data = item.trim().split("=");
return { ...res, [data[0]]: data[1] };
}, {});
}
However, if you actually want to look for a specific key and return its value, an array reducer isn't the best approach and your function would look rather different.
Code is:
const foo = (foo: string) => {
const result = []
result.push(foo)
}
I get the following TS error:
[ts] Argument of type 'string' is not assignable to parameter of type 'never'.
What am I doing wrong? Is this a bug?
All you have to do is define your result as a string array, like the following:
const result : string[] = [];
Without defining the array type, it by default will be never. So when you tried to add a string to it, it was a type mismatch, and so it threw the error you saw.
Another way is:
const result: any[] = [];
This seems to be some strange behavior in typescript that they are stuck with for legacy reasons. If you have the code:
const result = []
Usually it would be treated as if you wrote:
const result:any[] = []
however, if you have both noImplicitAny FALSE, AND strictNullChecks TRUE in your tsconfig, it is treated as:
const result:never[] = []
This behavior defies all logic, IMHO. Turning on null checks changes the entry types of an array?? And then turning on noImplicitAny actually restores the use of any without any warnings??
When you truly have an array of any, you shouldn't need to indicate it with extra code.
I got the same error in React function component, using useState hook.
The solution was to declare the type of useState at initialisation using angle brackets:
// Example: type of useState is an array of string
const [items , setItems] = useState<string[]>([]);
I was having same error In ReactJS statless function while using ReactJs Hook
useState.
I wanted to set state of an object array , so if I use the following way
const [items , setItems] = useState([]);
and update the state like this:
const item = { id : new Date().getTime() , text : 'New Text' };
setItems([ item , ...items ]);
I was getting error:
Argument of type '{ id: number; text: any }' is not assignable to parameter of type 'never'
but if do it like this,
const [items , setItems] = useState([{}]);
Error is gone but there is an item at 0 index which don't have any data(don't want that).
so the solution I found is:
const [items , setItems] = useState([] as any);
const foo = (foo: string) => {
const result: string[] = []
result.push(foo)
}
You needed specify what the array is since result = [] has a return type of any[]. Typically you want to avoid any types since they are meant to be used as an "Escape hatch" according to Microsoft.
The result is an object that is an array of string.
The solution i found was
const [files, setFiles] = useState([] as any);
I was able to get past this by using the Array keyword instead of empty brackets:
const enhancers: Array<any> = [];
Use:
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension())
}
Error: Argument of type 'any' is not assignable to parameter of type 'never'.
In tsconfig.json -
"noImplicitReturns": false,
"strictNullChecks":false,
Solution: type as 'never'
Remove "strictNullChecks": true from "compilerOptions" or set it to false in the tsconfig.json file of your Ng app. These errors will go away like anything and your app would compile successfully.
Disclaimer: This is just a workaround. This error appears only when the null checks are not handled properly which in any case is not a good way to get things done.
I got the error when defining (initialising) an array as follows:
let mainMenu: menuObjectInterface[] | [] = [];
The code I got the problem in:
let mainMenu: menuObjectInterface[] | [] = [];
dbresult.rows.forEach((m) => {
if (!mainMenu.find((e) => e.menucode === m.menucode)) {
// Not found in mainMenu, yet
mainMenu.push({menucode: m.menucode, menudescription: m.menudescription}) // Here the error
}
})
The error was: TS2322: Type 'any' is not assignable to type 'never'
The reason was that the array was initialised with also the option of an empty array.
Typescript saw a push to a type which also can be empty. Hence the error.
Changing the line to this fixed the error:
let mainMenu: menuObjectInterface[] = [];
You need to type result to an array of string const result: string[] = [];.
One more reason for the error.
if you are exporting after wrapping component with connect()() then props may give typescript errorSolution: I didn't explore much as I had the option of replacing connect function with useSelector hook
for example
/* Comp.tsx */
interface IComp {
a: number
}
const Comp = ({a}:IComp) => <div>{a}</div>
/* **
below line is culprit, you are exporting default the return
value of Connect and there is no types added to that return
value of that connect()(Comp)
** */
export default connect()(Comp)
--
/* App.tsx */
const App = () => {
/** below line gives same error
[ts] Argument of type 'number' is not assignable to
parameter of type 'never' */
return <Comp a={3} />
}
you could also add as string[]
const foo = (foo: string) => {
const result = []
(result as string[]).push(foo)
}
I did it when it was part of an object
let complexObj = {
arrData : [],
anotherKey: anotherValue
...
}
(arrData as string[]).push('text')
in latest versions of angular, you have to define the type of the variables:
if it is a string, you must do like that:
public message : string ="";
if it is a number:
public n : number=0;
if a table of string:
public tab: string[] = [];
if a table of number:
public tab: number[]=[];
if a mixed table:
public tab: any[] = [];
.......etc (for other type of variables)
if you don't define type of variable: by default the type is never
NB: in your case, you must know the type of variables that your table must contain, and choose the right option (like option 3 ,4 5 ).
This error occurs when you set the value of a property in an object when you haven't set the properties of the object. Declare a type for the object with its properties, then assign the type when you instantiated the object. Now you can set the values of the properties without this error.
I had this error for useRef
before with error:
const revealRefs = useRef([]);
const addToRefs = (el) => {
if (el && !revealRefs.current.includes(el)) {
revealRefs.current.push(el);
}
};
after without error:
const revealRefs = useRef<HTMLElement[]>([]);
const addToRefs = (el: HTMLElement | null) => {
if (el && !revealRefs.current.includes(el)) {
revealRefs.current.push(el);
}
};
i wrote a custom hook to interact with the sessionStorage in React. Currently I don't like that I can just arbitrarily write any key-value pair in there. For testing and debugging purposes I would like to introduce a form of TypeSafety and I was thinking about maybe using a Union Type rather then the Generic.
I basically want to achieve two goals.
check if the key is a valid key that is allowed to be put into the sessionStorage
if the key is allowed make sure the type of the value is correct.
Does anyone have any idea how you would go about implementing these sort of checks in Typescript. Any help is appreciated.
export function useSessionStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.sessionStorage.getItem(key);// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue] as const;
}
In case someone stumbles across this question, I ended up finding how this is implemented in Typescript. You can achieve the desired effect by the use of the typeof operator on the generic.
basically you first define a interface or type:
type Allowed = {
item1: string
item2: boolean
item3: number
}
Then you can modify the function using the keyof operator
function useSessionStorage<T extends keyof Allowed>(key: T, initialValue: Allowed[T]){
{...}
}
the use of key of is better described here: https://www.typescriptlang.org/docs/handbook/advanced-types.html.
Hope this helps other people that might be struggling with this.
I am kind of new to TypeScript and I am trying to get rid of all any types.
Problem:
Within the React Component, I loop over an array of objects and extract a key/value pair.The components receives tags, tagKeys props as follows:
tags: [{ name: "Some tag" }] OR [{ platform: { name: "Some tag" } }];
tagKeys: ["name"] OR tagKeys: ["platform", "name"]
If I run this script (below), I get this error in the console:
Element implicitly has an 'any' type because index expression is not of type 'number'
type Props = {
tags: any[];
tagKeys: string[];
};
const Tags: React.FC<Props> = ({ tags, tagKeys }: PropsWithChildren<Props>) => {
const renderTags = (): React.ReactNode => {
return tags.map(tag => {
let key = "";
// Loop through tagKeys in the tags array to extract the tag
for (let i = 0; i < tagKeys.length; i++) {
key = i === 0 ? tag[tagKeys[i]] : key[tagKeys[i]];
}
return <Tag key={`tag-${key}`} tag={key} />;
});
};
return <StyledTags>{renderTags()}</StyledTags>;
};
If I change the line inside the for loop to...
key = i === 0 ? tag[tagKeys[i] as any] : key[tagKeys[i] as any];
...the script runs but I want to get rid of the any type.
Question:
Is there a way to set the type without specifying how the received props array tags will look?
I want to be able to pass arrays with different kinds of structure and extract a key/value pair.
There two issues to watch that we'll need to be clear about.
First, you asked:
Is there a way to set the type without specifying how the received props array tags will look?
type Props = {
tags: any[]; // This is the answer to your direct question
tagKeys: string[];
};
Secondly, having handled the above, TypeScript is actually complaining about your index.As you'll agree, reactjs uses the unique key to render each <Tag>.
// TypeScript is actually warning you about this.
// Note that initially, `key` is of type `string` (implicitly or indirectly )
let key = "";
// Within your loop, you assign `key` to be of type `object`
key = i === 0 ? tag[tagKeys[i]] : key[tagKeys[i]];
// So the above line says that `key` can be `string` or `object`
// Thus TypeScript tells you that...
// implicitly your index (key) expression is of type any
To handle that TypeScript error, update as follows:
let key: any = "";
Kindly make the necessary updates, and let me know how it goes for you.
key can be either a string or an object during it's lifetime, so my first intuition would be to declare it as such:
let key: object | string = "";
But TS is not happy with that, so my second try would be:
type NestedObject = { [key: string]: NestedObject } | string
let key: NestedObject = '';
key = { platform: { name: 'dd' } }
key = key.platform.name
But TS is not happy with the last line either, so at this time I would just give up:
let key: any = '';
Please see the code below. I have to do two casting to avoid any flow error. If I use the commented out lines instead, it complains.
playground
/* #flow */
import * as React from "react";
type ConfObj = { label: string };
type Conf = React.Node | ConfObj;
type MyComponentProp = {
confs: Array<Conf>,
}
export default function MyComponent({
confs = [],
}: MyComponentProp) {
const items = confs.map((item, idx) => {
if (React.isValidElement(item)) {
// return React.cloneElement(item, {
return React.cloneElement(((item: any): React.Element<*>), {
key: idx.toString(),
});
}
const item2 = ((item: any): ConfObj);
return <span>{item2.label}</span>;
// return <span>{item.label}</span>;
});
return <div>items</div>
}
Is there a better way to do this to avoid the casting. Is there a better way to write isValidElement, so flow can deduce the type once the if condition matches. For example, if it is a valid react element, why do I need to cast it? or if it not, why accessing label gives error?
An item is of type Conf (which is Node | ConfObj)
When you enter the if statement, flow doesn't know for sure that item is a valid Element<*> (this could be known by flow I think tho), so you have to explicitly typecast it.
The <span>{item.label}</span> has the same problem. You also have to explicitly typecast it to a ConfObj, because a Node doesn't have a label attribute.