Typescript 如何检查对象是否符合类型要求

14

我有以下问题。假设我有一个类型:

type A = {
    prop1: string,
    prop2: {
        prop3: string
    }
}

我从外部服务接收到一些 JSON 对象,我想要验证这个 JSON 是否符合类型 A

function isA(obj:any): boolean {
    // What should be here?
}

如果我的obj长这样:
{
 prop1: "Hello",
 prop2: {
    prop3: "World"
 }
}

或者

{
     prop1: "Hello",
     prop2: {
        prop3: "World"
     },
     moreProps: "I don't care about"
}

该函数会返回true,但对于像这样的内容会返回false。
{
     foo: "Hello",
     bar: {
        prop3: "World"
     }
}

什么是实现这一点的最简单方法?谢谢。

JavaScript的typeof对你有用吗? - paulsm4
我推荐使用Zod进行运行时类型检查。https://dev.to/diballesteros/supercharge-your-app-with-runtime-type-checking-using-zod-4no - undefined
2个回答

8
  1. 使用类型保护,这样Typescript会在类型检查时自动缩小类型范围

要使用类型保护,您应该将isA函数的返回类型更改为obj is A

总体上,您的类型验证函数应该如下所示:

function isA(obj: unknown): obj is A {
    // return boolean here
}
  1. 使用typeof运算符检查属性

typeof将返回一个字符串值,告诉您变量的类型。(文档)

在本例中,对于A,您可以这样做:

function isA(obj: unknown): obj is A {
    return (
        obj &&
        typeof obj === 'object' &&
        typeof obj['prop1'] === 'string' &&
        obj['prop2'] &&
        typeof obj['prop2'] === 'object' &&
        typeof obj['prop2']['prop3'] === 'string'
    );
}

这可能不是世界上最易读的内容,如果需要,您可以将其分解为组件,并对每个检查进行注释。

然而,需要注意的一点是,null的类型实际上是'object',所以您不能简单地检查typeof obj['prop2'] === 'object'并继续执行,您还需要检查它是否存在,因为它仍然可能是null

此时,您不仅可以在运行时正确验证,而且TypeScript现在可以通过将isA返回true来缩小obj的类型为A,从而改善其类型检查。


2
我觉得这种方式有点奇怪...每次 A 改变时,这个也需要改变。很容易忘记添加属性。难道没有更通用的方法吗?假设有一个类型定义:type A = { prop1: string, prop2: { prop3: string } } 我会认为语言会提供一种简单的方法来检查某个东西是否符合该定义。无论如何,感谢您的回答。 - mario595
1
我完全理解你的意思。事实是,TypeScript 的一个明确的设计目标之一是使用“完全可擦除”的类型系统。这与你所要求的相冲突,因为运行时功能完全取决于类型。 - casieber

1

这也不是一个完美的解决方案,但另一个选择是定义一个“模板对象”,对其进行运行时比较,如下所示:

// A const object used to define a type and to serve as a template for runtime comparison.
const myTemplateObject = {
  prop1: "",
  prop2: 12,
  prop3: 14 as string | number,
  prop4: {
    potatoes: "",
    carrots: 0,
  },
};

// So you can use the type in the rest of your code. Or just define it explicitly and make the object above an instance of it.
export type myType = typeof myTemplateObject;

export function matchesType(
  object: Record<string, unknown>,
  templateObject: Record<string, unknown>,
) {
  for (const key in templateObject) {
    const templatePropValue = templateObject[key];
    const templatePropType = templatePropValue;
    switch (templatePropType) {
      case "function":
      // fall-through
      case "symbol":
      // fall-through
      case "undefined":
        throw new Error(
          `matchesType function does not support template objects with ${templatePropType} fields`,
        );
      // or return false if you prefer
      case "bigint":
      case "boolean":
      case "number":
      case "string":
        return templatePropType === typeof object[key];
      case "object":
        const objectPropValue = object[key];
        if (typeof objectPropValue === "object" && objectPropValue !== null) {
          return matchesType(
            objectPropValue as Record<string, unknown>,
            templatePropValue as Record<string, unknown>,
          );
        } else {
          return false;
        }
    }
  }
  return true;
}

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