typescript “未知”类型的参数不能分配给“{}”类型的参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/56485202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:49:48  来源:igfitidea点击:

Argument of type 'unknown' is not assignable to parameter of type '{}'

reactjstypescript

提问by jcalz

Here is my code

这是我的代码

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

In the line In the line I get this error ".map (v => Object.values (v) .flat ())" I get this error Argument of type 'unknown' is not assignable to parameter of type '{}'.How can this problem be solved?

在行 在行中,我收到此错误“.map (v => Object.values (v) .flat ())” 我收到此错误“未知”类型的参数不可分配给“{}”类型的参数.如何解决这个问题?

回答by jcalz

The problem here is that you need to help TypeScript understand the types of the objects you're dealing with. The fetchAPI 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.

这里的问题是你需要帮助 TypeScript 理解你正在处理的对象的类型。该fetchAPI无法事先知道返回的对象的形状将是,所以你必须定义,并断言他结果符合它。

Looking at what's at https://foo0022.firebaseio.com/.json, I'd suggest something like the following:

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

interface ResObj {
  Mens: {
    Hat: Clothing[];
    Hymanet: 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:

但当然这是否准确取决于某种 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");
  }

Now ResObjwill be known as type ResObjand 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:

现在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(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 typesto tease out the array properties.

如果您以前从未使用过 TypeScript,那么这些函数类型可能会令人困惑,尤其是因为它们依赖条件类型来梳理数组属性。

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 ResArris an array of Clothingobjects.

并且没有错误,编译器知道这ResArr是一个Clothing对象数组。

Link to code

代码链接

Okay, hope that helps; good luck!

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

回答by Shaun Luttin

Problem

问题

Res.json()returns a value of type any, and when Object.valuesreceives input of type any, it returns an unknown[]. When strictNullChecksis on, TypeScript will not let us assign a value of type unknownto a parameter of type {}.

Res.json()返回一个类型的值any,当Object.values接收到类型的输入时any,它返回一个unknown[]. 当strictNullChecks打开时,TypeScript 不会让我们将 type 的值分配给 typeunknown的参数{}

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

两种可能的解决方案

  1. Disable strictNullChecks(not recommended).
  2. Add a type to the ResObj.
  1. 禁用strictNullChecks(不推荐)。
  2. 将类型添加到ResObj.

The latter option looks like this:

后一个选项如下所示:

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

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