Argument of type 'unknown' is not assignable to parameter of type '{}'
The problem here is that you need to help TypeScript understand the types of the objects you're dealing with. The fetch
API can't know in advance what the shape of the returned objects are going to be, so you have to define it and assert that he results conform to it.
Looking at what's at https://foo0022.firebaseio.com/.json, I'd suggest something like the following:
interface ResObj {
Mens: {
Hat: Clothing[];
Jacket: Clothing[];
Pants: Clothing[];
Shoes: Clothing[];
Suit: Clothing[];
};
New: Clothing[];
}
interface Clothing {
agility: boolean[];
alt: string;
color: string[][];
id: string;
location?: string; // fix this
Location?: string; // fix this
material: string;
price: string[][];
prodState: string;
saiz: string[][];
shipping: string;
sold: string;
src: string[][];
title: string;
to: string;
}
But of course whether or not that's accurate depends on some kind of API documentation. Assuming that's right, you can go a bit further:
const Res = await fetch(`https://foo0022.firebaseio.com/.json`);
const ResObj: ResObj | undefined = await Res.json();
if (!Res.ok || !ResObj) {
throw new Error("Page Not Found 404");
}
Now ResObj
will be known as type ResObj
and you can start manipulating it. One issue is that the standard library's typings for Object.values()
and Array.prototype.flat()
don't reflect what you're doing with them. We can build some custom typings for them... but in this case I'll just wrap them with new functions whose types match:
// return an array of all object values...
// if the object is already an array, the output is the same type.
// otherwise it's the union of all the known property types
function vals<T extends object>(
arr: T
): Array<T extends Array<infer U> ? U : T[keyof T]> {
return Object.values(arr); // need es2017 lib for this
}
// Flatten an array by one level...
function flat<T>(
arr: Array<T>
): Array<Extract<T, any[]>[number] | Exclude<T, any[]>> {
return arr.flat(); // need esnext lib for this
}
Those functions typings might be confusing if you've never used TypeScript before, especially since they rely on conditional types to tease out the array properties.
Then we can rewrite your code like this:
const ResArr = flat(vals(ResObj).map(v => flat(vals(v)))).filter(
({ title }) => title.includes(Search)
);
And there are no errors, and the compiler understands that ResArr
is an array of Clothing
objects.
Link to code
Okay, hope that helps; good luck!
Problem
Res.json()
returns a value of type any
, and when Object.values
receives input of type any
, it returns an unknown[]
. When strictNullChecks
is on, TypeScript will not let us assign a value of type unknown
to a parameter of type {}
.
That explanation is also inline with the comments.
const func = async () => {
const Res = await fetch(`https://foo0022.firebaseio.com/.json`);
/**
* The ResObj is that `Res.json()` returns is of type `any`.
*/
const ResObj = await Res.json();
if (!Res.ok || !ResObj) {
throw new Error("Page Not Found 404");
}
/**
* When we pass Object.values a type of `any`,
* it produces an array of type `unknown[]`.
*/
const unknownArray = Object.values(ResObj);
/**
* `Object.values` has two signatures:
*
* * `values(o: {}): any[];`
* * `values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];`
*
* When `strictNullCheck` is `true`, we cannot assign `unknown` to `{}`.
*/
const ResArr = unknownArray.map(unknownItem => Object.values(unknownItem));
};
Two Possible Solutions
- Disable
strictNullChecks
(not recommended). - Add a type to the
ResObj
.
The latter option looks like this:
type MyKnownType = {
prop1: string;
prop2: number;
prop3: boolean;
};
const ResObj: MyKnownType = await Res.json();