Typescript - 如何最好地对对象属性进行类型检查并在空值时抛出错误?

3

假设我们有这段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);

目前我们有一堆if语句相互跟随,感觉应该有更好的方法来处理,你有什么简洁的建议吗?


你可以编写一个断言函数,就像这样;这是否满足您的需求?如果是,我可以撰写一篇解释它的答案;如果不是,那么我错过了什么? - jcalz
@jcalz 是的,它符合我所需的。感谢您的快速回复! - Gerrol
1个回答

3
你可以编写一个自定义断言函数,该函数遍历其参数的所有属性,并在任何属性为null时抛出异常;而返回的断言谓词(形式为asserts 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");
    });
}

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

assertNoPropsAreNull() 的调用签名是 generic,其在 obj 的类型 T 中,并且返回类型为 asserts obj is { [K in keyof T]: Exclude<T[K], null> }。这是一种mapped type,通过Exclude<T, U>实用类型仅从每个属性类型中剥离了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) 之前,value 的类型为 MyType,之后已被缩小为 {key1?: string, key2?: string, key3?: string}。这种类型与您的 MyTypeNotNull 类型在结构上完全相同,因此 logMyType() 可以无警告地接受它。
让我们确保运行时行为也是可接受的:
run({ key1: 'myKey1' }); // {key1: "myKey1"}

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

这看起来也不错。在第一种情况下,断言成功,因此使用与 MyTypeNotNull 兼容的输入调用了 logMyType(),而在第二种情况下,断言失败,根本没有调用 logMyType()

代码游乐场链接


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