泛型TypeScript函数,仅接受例如号码|undefined,never,never,

0ejtzxu1  于 2023-10-22  发布在  TypeScript
关注(0)|答案(1)|浏览(110)

我试图创建一个实用程序类,当某些东西可能未定义时抛出异常,如throwIfUndefined(array[index]).function()throwIfUndefined(obj.key).function()。这是为了使我的代码更简洁,因为使用例如。如果条件和手动抛出是非常冗长的。
我尝试了这个尝试:

export const throwIfUndefined = <T>(v: T | undefined): T => {
  if (v === undefined) {
    throw new Error("Unexpected undefined");
  } else {
    return v;
  }
};

这主要是工作:

const x = Math.random() < 0.5 ? undefined : 1;
throwIfUndefined(x); // works: type-checks as expected
throwIfUndefined(1); // broken: should give type error because the input can never be undefined

如何修复最后一种情况,以便TypeScript在不必要地使用throwIfUndefined时捕获?
我试过使用条件类型,但我无法理解它们在这里是如何工作的。
编辑:我不喜欢写的代码的例子

const item = array[index];
if (!item) return;

return item.function() ? 1 : 2;

你必须获取的数组项越多,情况就越糟糕。我宁愿有这样的简写(避免额外的条件和额外的变量):

return throwIfUndefined(array[index]).function() ? 1 : 2
nukf8bse

nukf8bse1#

我不知道这是不是一个好主意,但可以采取的一种方法是约束泛型类型,以便undefined需要可分配给它。这将是一个 * 下限 * 约束,就像说T super undefined而不是T extends ⋯,但TypeScript本身不支持下限约束(在microsoft/TypeScript#14520处有一个功能请求)。您可以通过编写以下形式的递归约束来模拟它:

export const throwIfUndefined =
  <T extends (undefined extends T ? unknown : never)>(v: T) => {
    if (v === undefined) {
      throw new Error("Unexpected undefined");
    } else {
      return v;
    }
  };

这里Tv的类型,函数的返回类型是T & ({} | null)。类型{} | null本质上是“除了undefined之外的所有内容”,所以T & ({} | null)本质上是“除了删除undefined之外的T”。让我们测试一下:

const x = Math.random() < 0.5 ? undefined : 1;
const y = throwIfUndefined(x); // okay;
y // 1

throwIfUndefined(1); // error
// Argument of type 'number' is not assignable to parameter of type 'never'.

对于好的调用,输入类型为1 | undefined,输出类型为1。对于错误的调用,编译器会抱怨number不能赋值给never。这可能是一个令人困惑的错误,但是没有microsoft/TypeScript#23689中请求的 invalid typesthrow types 也没有更好的方法。如果你使用这种方法,你可能会想要很好地记录这个函数,让人们知道发生了什么。
Playground链接到代码

相关问题