无法将类型'unknown'的参数分配给类型'{}'

时间:2019-06-06 21:15:31

标签: reactjs typescript

这是我的代码

 const Res = await fetch(`https://foo0022.firebaseio.com/.json`);
        const ResObj = await Res.json();
        if (!Res.ok || !ResObj) { 
          throw new Error("Page Not Found 404");
        } 
        const ResArr = await Object.values(ResObj)
            .map(v => Object.values(v).flat())//error
            .flat()
            .filter(({ title }) => title.includes(Search))

在该行中,我得到这个错误“ .map(v => Object.values(v).flat())”,我得到了这个错误类型'unknown'的参数不能分配给类型'的参数{}'。如何解决此问题?

2 个答案:

答案 0 :(得分:3)

这里的问题是,您需要帮助TypeScript了解您要处理的对象的类型。 fetch API无法预先知道返回对象的形状,因此您必须对其进行定义并断言其结果与之相符。

看看https://foo0022.firebaseio.com/.json的内容,我建议如下内容:

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

但是,当然,这是否正确取决于某种API文档。假设没错,您可以走得更远:

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

现在ResObj将被称为类型ResObj,您可以开始对其进行操作。一个问题是标准库为Object.values()Array.prototype.flat()键入的内容不能反映您对它们的处理方式。我们可以为它们建立一些自定义类型...但是在这种情况下,我将使用类型匹配的新函数包装它们:

  // 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(vals); // 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
  }

如果您以前从未使用过TypeScript,那么这些函数的类型输入可能会令人困惑,尤其是因为它们依赖于conditional types来弄清数组属性。

然后我们可以像这样重写您的代码:

  const ResArr = flat(vals(ResObj).map(v => flat(vals(v)))).filter(
    ({ title }) => title.includes(Search)
  );

没有错误,编译器理解ResArrClothing对象的数组。

Link to code

好的,希望能有所帮助;祝你好运!

答案 1 :(得分:0)

问题

Res.json()返回类型为any的值,并且当Object.values收到类型为any的输入时,它返回一个unknown[]。启用strictNullChecks时,TypeScript不允许我们将类型unknown的值分配给类型{}的参数。

该解释也与评论一致。

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

两个可能的解决方案

  1. 禁用strictNullChecks(不推荐)。
  2. 将类型添加到ResObj

后一个选项如下:

type MyKnownType = {
    prop1: string;
    prop2: number;
    prop3: boolean;
};

const ResObj: MyKnownType = await Res.json();