有没有一种TypeScript编译器选项可以强制对对象属性进行类型检查?

3
为了编写更加健壮的代码,我正在考虑在我的基于Web的项目中使用TypeScript。虽然我在这门语言方面的经验相对较少,但我已经遇到了一个难以搜索的问题。
请看下面的TypeScript代码:
public static getResponseCode(resObj: object): number {
    let c: number = resObj["c"]
    return c;
}

有没有办法让TypeScript编译器提醒我,resObj ["c"] 可能不是一个数字,并且需要先检查它是否是数字?例如,我可以强制TypeScript将代码重写为这样(或类似的代码)吗?

public static getResponseCode(resObj: object): number {
    if (typeof resObj["c"] !== "number") { return APIResponseCode.UnknownFailure; }

    let c: number = resObj["c"]
    return c;
}

我认为我之所以得到这样的结果,是因为resObj["c"]的类型是any。在这里我该怎么做?TypeScript中是否有常用模式可用?


使用 resObj.c 替代 resObj["c"] 来获取属性 c 的类型检查。 - user663031
类型 object 没有属性 c。@torazaburo - Shaun Luttin
2个回答

2

你可以启用noImplicitAny编译器选项。编译器会在隐式使用任何内容时发出警告。

我们在tsconfig.json中设置了noImplictAny。

{
    "compilerOptions": {
        "noImplicitAny": true
    }
}

或者我们可以在命令行中这样设置:tsc --noImplicitAny

这将强制你做些什么。

例如,你可以明确地使用 any。

public static getResponseCode(resObj: object): number {
    const resAny = resObj as any;
    if (resAny.c && typeof resAny.c !== "number") {
        return APIResponseCode.UnknownFailure;
    }

    const c: number = resAny.c;
    return c;
}

或者你可以使用一个带有联合类型和类型守卫的接口。

interface ResObj {
    c: boolean | string | number | object;
}

public static getResponseCode2(resObj: ResObj): number {

    if (typeof resObj.c !== "number") {
        return APIResponseCode.UnknownFailure;
    }

    const c: number = resObj.c;
    return c;
}

1
这似乎正是我想要的。非常感谢。您能否建议我是否应该使用“object”类型来传递对“JSON.parse”调用的结果?还是应该使用“any”? - Luke Joshua Park
我正在使用TypeScript生成客户端JavaScript。所有与我的API的请求/响应都通过jQuery AJAX作为传统的JavaScript JSON对象发送。我的问题是,当传递这些请求和响应对象时,使用“object”类型还是使用“any”类型更合理?由于它们差异很大,我无法为所有可能的请求和响应制作接口。你有什么建议? - Luke Joshua Park
@LukePark JSON.parse() 返回一个 object,所以我会使用 object 而不是 any,因为 any 也可以是原始类型,比如 boolean,而 JSON.parse() 永远不会返回这种类型。 - Shaun Luttin
那么当我构建请求对象时怎么办?如果我做类似 reqObj.t = "asd"; 的事情,那么我会得到“属性 't' 在类型对象上不存在...”等等的错误提示。 - Luke Joshua Park
这取决于你是否想让编译器帮助你。如果你要使用 any,那么编译器将停止为你提供帮助,因为 any 让你退出了静态类型检查。所以,在 anyobject(或其他某种类型)之间做出选择是一种权衡。我们需要在使用动态语言开发的便利性和使用带类型语言开发的安全性之间做出选择。 - Shaun Luttin
听起来像是我想要开发时的便利性,但在接收数据时又需要类型安全... 叹气,我总是陷入这种情况。谢谢你的帮助! - Luke Joshua Park

2

TypeScript不会进行运行时类型检查。根据您想要做什么,有几个选项可供选择。第一种方法是为resObj定义一个接口:

interface ResObj { c: number; }

现在
public static getResponseCode(resObj: ResObj): number {
  let c: number = resObj.c;
  return c;
}

如果你传递给getResponseCode的内容与ResObj不匹配(例如c属性不是一个数字),那么你的代码现在将无法编译。

如果你想让c可以是数字或字符串,那么可以使用联合类型:

interface ResObj { c: number | string; }

您上面的getResponseCode原始版本现在将无法编译。TypeScript会抱怨它不知道如何将string | number转换为number

如果按照您提出的方式编写代码:

public static getResponseCode(resObj: ResObj): number {
  if (typeof resObj.c !== "number") { return APIResponseCode.UnknownFailure; }

  let c: number = resObj.c;
  return c;
}

由于TypeScript的流分析检测到typeof检查,并且知道在达到return语句时c只能是一个数字,因此TypeScript将感到高兴。

对于更复杂的情况,您可以使用所谓的类型保护,它明确告诉TypeScript特定函数保证其参数的特定类型:

function isNumber(c: string | number): c is number {
  return typeof c === 'number';
}

现在,您可以编写代码。
public static getResponseCode(resObj: ResObj): number {
  if (!isNumber(resObj.c)) { return APIResponseCode.UnknownFailure; }

  let c: number = resObj.c;
  return c;
}

谢谢您的回答。我想我现在只能坚持使用 typeof 检查,因为我传递给 getResponseCode 的对象是在运行时接收到的,并且如您所说,没有运行时类型检查。再次感谢。 - Luke Joshua Park

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接