Typescript - 键入检查对象属性并在 null 时抛出的最佳方法是什么?

Typescript - What is the best way to type check an object properties and throw upon null?

提问人:Gerrol 提问时间:8/4/2022 最后编辑:Gerrol 更新时间:8/4/2022 访问量:706

问:

假设我们有这个 Typescript 代码,那么编写 TODO 部分的最佳方法是什么?我和我的团队正在寻找一种优雅的方式来做到这一点

type MyType = {
  key1?: string | null
  key2?: string | null
  key3?: string | null
  // ...
  // ...
  // ...
};

type MyTypeNotNull = {
  key1?: string
  key2?: string
  key3?: string
  // ...
  // ...
  // ...
};

function logMyType(type: MyTypeNotNull) {
  console.log(type);
}

const myType: MyType = {
  key1: 'myKey1',
  // key2 is omitted
  key3: null,
  // ...
  // ...
  // ...
};

const run = (value: MyType) => {
  /* TODO
    - throw when a prop is null
    - make myType pass the type check of logMyType
  */

  logMyType(value); // Error Type 'string | null | undefined' is not assignable to type 'string | undefined'
};

run(myType);

目前我们有一堆如果互相关注,并且觉得它应该存在一种更好的方法来做到这一点,你有什么干净的建议吗?

TypeScript 对象 抛出

评论

0赞 jcalz 8/4/2022
你可以像这样编写一个断言函数;这能满足您的需求吗?如果是这样,我可以写一个答案来解释它;如果没有,我错过了什么?
0赞 Gerrol 8/4/2022
@jcalz 是的,它符合我的需求。感谢您的快速回复!

答:

4赞 jcalz 8/4/2022 #1

您可以编写一个自定义断言函数,该函数遍历其所有参数的属性,如果其中任何一个属性为;返回的断言谓词(形式为 )会将参数的类型缩小到具有所有非 null 属性的版本。下面是一种方法:nullasserts xxx is YYY

function assertNoPropsAreNull<T extends object>(obj: T): asserts obj is
    { [K in keyof T]: Exclude<T[K], null> } {
    Object.entries(obj).forEach(([k, v]) => {
        if (v === null)
            throw new Error("OH NOEZ, PROP \"" + k + "\" IS NULL");
    });
}

的实现使用 Object.entries() 方法获取参数的键/值对数组,如果发现值为 null 的任何值(并在错误消息中提及键),则抛出错误。assertNoPropsAreNull()arg

的调用签名在 中是泛型的,类型为 ,返回类型为 。这是一个映射类型,它只是通过 Exclude<T, U> 实用程序类型从每个属性类型中剥离出来。assertNoPropsAreNull()Tobjasserts obj is { [K in keyof T]: Exclude<T[K], null> }null

让我们来测试一下:

const run = (value: MyType) => {

  value; // (parameter) value: MyType

  assertNoPropsAreNull(value)
  value; /* (parameter) value: {
    key1?: string | undefined;
    key2?: string | undefined;
    key3?: string | undefined;
  } */

  logMyType(value); // okay    
};

编译器行为看起来不错。在调用 之前,类型是 ,之后它被缩小到 。此类型在结构上与您的类型相同,因此接受它而不发出警告。assertNoPropsAreNull(value)valueMyType{key1?: string, key2?: string, key3?: string}MyTypeNotNulllogMyType()

让我们确保运行时行为也是可以接受的:

run({ key1: 'myKey1' }); // {key1: "myKey1"}

run({ key1: 'myKey1', key3: null }); // 💥 OH NOEZ, PROP "key3" IS NULL

这看起来也不错。在第一种情况下,断言成功,因此使用 -compatible 输入调用,而在第二种情况下,断言失败,根本不被调用。logMyType()MyTypeNotNulllogMyType()

Playground 代码链接