首页 > 解决方案 > TS7017 暗示任何类型 + 类型推断

问题描述

这是错误的浓缩片段:

export default function formatSql(this: EscapeFunctions, sqlQuery: string, values: QueryParams) {

    if (isPlainObject(values)) {
        console.log(values[p]); // <-- Element implicitly has an 'any' type because type 'QueryParams' has no index signature.
    } else if (Array.isArray(values)) {
        // ...
    } else {
        throw new Error(`Unsupported values type`);
    }
    // ...
}

QueryParams定义为:

export type QueryParams = StringMap | any[];
export interface StringMap {
    [_:string]: any,
}

所以,StringMap如果我没记错的话,有一个“索引签名”,isPlainObject定义为:

export function isPlainObject(obj: any): obj is object  {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

所以我认为isPlainObject检查会排除any[]类型,因此values应该推断为StringMap,但这似乎不是正在发生的事情。

即使我isPlainObject返回obj is StringMap,Typescript 仍然会抱怨。

怎么会?有什么方法可以在不进行所有类型转换的情况下完成这项工作?

标签: typescripttype-inference

解决方案


从技术上讲, Array 仍然符合 type {[key: string]: any}。您可以通过执行来验证这一点const test: StringMap = [];。TypeScript 编译器不会抱怨。因此,您首先必须排除values是数组的可能性。

接下来,您的函数isPlainObject将返回类型定义为obj is object. 这太通用了,并且会导致您的代码块“忘记”该对象具有索引签名。它需要是obj is StringMap

所以在实践中,你需要做两件事:

  1. 在您的第一if条语句中,检查值是否为数组
  2. isPlainObject将您的返回类型声明更改为obj is StringMap

基本上它看起来像这样:

export function isPlainObject(obj: any): obj is StringMap  {
  // ...
}

export default function formatSql(values: QueryParams) {
  if (Array.isArray(values)) {
    // ...
  } else if (isPlainObject(values)) {
    console.log(values[p]);
  } else {
    throw new Error(`Unsupported values type`);
  }
}

推荐阅读