【问题标题】:TypeScript Unsafe use of expression of type 'any' ERRORTypeScript 不安全地使用“任何”类型的表达式错误
【发布时间】:2022-01-14 23:28:45
【问题描述】:

我是 TypeScript 新手,我的场景描述如下:

我有一个这样的类型:

 type response = {
  totalQuantity: number;
  numberOfCatogory: number
  appleQuantity: number;
  bananaQuantity: number;
  pearQuantity: number;
};

每个单独的水果数量都有验证,在本例中是 appleQuantity、bananaQuantity 和 pearQuantity,这意味着我需要从响应中获取每个单独的水果数量。

所以我创建了一个字符串数组来保存单个属性键,如下所示:
const individualParameters: string[] = ["appleQuantity", "bananaQuantity", "pearQuantity"]

函数逻辑如下:

for (const individualParameter of individualParameters) {
    let individualQuantity = response[individualParameter];
    ...
}

但是当我构建时,TSLint 在response[individualParameter] 处抛出错误

Unsafe use of expression of type 'any'

我认为是因为响应无法识别字符串数组元素的类型?
我是 TypeScript 新手,很好奇解决这个问题的好方法。

【问题讨论】:

  • 请分享可重现的示例。 individualQuantityindividualCartCountResponseParameter 未声明
  • 尝试使用const individualParameters= ["appleQuantity", "bananaQuantity", "pearQuantity"] as const 而不是const individualParameters: string[] = ["appleQuantity", "bananaQuantity", "pearQuantity"]

标签: typescript typescript-typings tslint


【解决方案1】:

所以我认为大多数人已经解释了发生了什么,我认为到目前为止我个人最喜欢的答案是captain-yossarian's answer。他很好地解释了这个问题:

因此,typescript 拒绝使用 response[individualParameter],因为 response 是具有严格键的类型,而 individualParameter 可能是任何字符串。

然而,这个问题可以通过告诉 Typescript 你正在定义一个包含字符串的数组来轻松解决,这些字符串也是你尝试访问的类型的键:

type response = {
  totalQuantity: number;
  numberOfCatogory: number
  appleQuantity: number;
  bananaQuantity: number;
  pearQuantity: number;
};

// this should be your non-null and defined object in reality
let response: response;

// just this one line below was altered
const individualParameters: (keyof response)[] = ["appleQuantity", "bananaQuantity", "pearQuantity"];

for (const individualParameter of individualParameters) {
    let individualQuantity = response[individualParameter];
}

(keyof response)[] 类型意味着您将始终拥有一个仅包含 response 类型键的数组。在我的示例中,此声明是多余的,因为编译器已经可以推断出您定义的所有属性都是该类型的键,但是例如,当 for 循环位于函数中时,情况就不同了。我假设你就是这种情况,否则你不会在第一种情况下问这个问题。

@captain-yossarian 是对的;根据 TS 约定,类型应该大写。

【讨论】:

    【解决方案2】:

    问题出在这一行:const individualParameters:string[] = ["appleQuantity", "bananaQuantity", "pearQuantity"]

    根据individualParameters的类型定义,每个元素都是一个string。这意味着即使这个"foo" 字符串也可以分配给类型string

    因此,typescript 拒绝使用 response[individualParameter],因为 response 是具有严格键的类型,而 individualParameter 可能是任何字符串。

    为了让它工作,你可以使用const assertion

    type response = {
      totalQuantity: number;
      numberOfCatogory: number
      appleQuantity: number;
      bananaQuantity: number;
      pearQuantity: number;
    };
    
    const individualParameters = ["appleQuantity", "bananaQuantity", "pearQuantity"] as const
    
    declare const response: response;
    
    for (const individualParameter of individualParameters) {
      let individualQuantity = response[individualParameter]; // ok
    
    }
    

    如果你不喜欢as const的方式,你可以使用额外的功能:

    type response = {
      totalQuantity: number;
      numberOfCatogory: number
      appleQuantity: number;
      bananaQuantity: number;
      pearQuantity: number;
    };
    
    
    declare const response: response;
    
    const iterator = <
      Obj extends Record<string, number>,
      Keys extends Array<keyof Obj>
    >(record: Obj, keys: Keys) => {
      for (const individualParameter of keys) {
        let individualQuantity = record[individualParameter];
      }
    }
    
    iterator(response, ['foo']) // expected error
    
    // works only with literal array
    iterator(response, ["appleQuantity", "bananaQuantity", "pearQuantity"]) // ok
    

    Playground

    Obj - 代表response 参数, Keys - 确保第二个数组参数包含有效的 Obj

    附:根据 TS 约定,类型应该大写。

    【讨论】:

    • 或者干脆把参数声明改成const individualParameters: (keyof response)[] = ["appleQuantity", "bananaQuantity", "pearQuantity"];吧?
    • @jboot 你是对的
    • @jboot 您可以将其作为单独的答案发布
    • 我做到了,谢谢。我也提到了你的答案,因为我认为你已经涵盖了大部分内容,它仍然是一个有效的答案。
    • @captain-yossarian 感谢您的详细解释!但我会接受 jboot 的回答作为更清洁的解决方案
    【解决方案3】:

    您需要使您的响应可索引。更多相关信息可以在here找到。

    在你的情况下,它可以通过使用这样的接口来完成:

    interface IResponse {
      [key: string]: number;  // <- This line makes your interface indexable
      totalQuantity: number;
      numberOfCatogory: number
      appleQuantity: number;
      bananaQuantity: number;
      pearQuantity: number;
    }
    

    现在您只需要确保您的响应实现了 IResponse 接口:

    const response: IResponse = {
      totalQuantity: 1,
      numberOfCatogory: 2,
      appleQuantity: 3,
      bananaQuantity: 4,
      pearQuantity: 5
    }
    

    在此之后,您可以使用字符串数组来获取单个水果的数量,如下所示:

    const individualParameters: string[] = ["appleQuantity", "bananaQuantity", "pearQuantity"];
    
    for (let individualParameter of individualParameters) {
        console.log(response[individualParameter]);
    }
    
    // Output:
    // 3
    // 4
    // 5
    

    【讨论】:

      【解决方案4】:

      一种简单的方法是您可以选择将类型的键作为字符串变量

      type response  = {
          [key: string]: number;
      };
      

      但如果你真的要修复 key 是 appleQuantity、bananaQuantity、blablabla 您可以尝试Mapped Type,点击此链接了解更多详情 https://www.typescriptlang.org/docs/handbook/2/mapped-types.html

      【讨论】:

        猜你喜欢
        • 2021-08-23
        • 2011-02-12
        • 2020-12-22
        • 1970-01-01
        • 1970-01-01
        • 2021-07-27
        • 2022-01-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多