首页 > 解决方案 > typescript 参数中允许的最小数量

问题描述

我有一个类似的功能:

function getBlogs(limit?: number) { ... }

limit只能是正整数,不能是0

我如何告诉打字稿这个数字只能大于 0?

标签: javascripttypescript

解决方案


有一个开放的建议microsoft/TypeScript#15480来支持“范围类型”,您可以在其中说诸如 type PositiveWholeNumbers = 1..9999999表示您的类型之类的东西。还有一个(关闭的)建议microsoft/TypeScript#4639来支持intuint-like 类型,它也可能在这里工作。遗憾的是,这些都没有实现,所以如果你想在类型系统中实现这一点,你需要做其他事情。


如果有一个相当小的上限(最多数千个),您可以手动生成允许值的并并将其推送到某个库文件中。这可以通过编程方式完成,因此您不必亲自输入它,但这与您执行的操作相同:

// Evaluate the following in JS and copy into your code somewhere
// "type AllowableValues = "+
//   Array.from({length: 5000}, (x, i) => String(i+1)+((i+1)%100?"":"\n")).join(" | ")+";";
// which produces:

type AllowableValues = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | // and so on

function getBlogs(limit?: AllowableValues) {
  console.log((limit || 0).toFixed());
}

从某种意义上说,这将起作用,编译器将看到这limit是联合(或undefined)中的某个数值,并且getBlogs()使用数字参数调用将对其进行验证:

getBlogs(100); // okay
getBlogs(1); // okay
getBlogs(1.5); // error!
// ----> ~~~
// Argument of type '1.5' is not assignable to 
// parameter of type 1 | 2 | 3 | ...
getBlogs(0); // error!
// ----> ~
// Argument of type '0' is not assignable to 
// parameter of type 1 | 2 | 3 | ...
getBlogs(-4);
// ----> ~~
// Argument of type '-4' is not assignable to 
// parameter of type 1 | 2 | 3 | ...

当然,超出范围的任何事情都会失败:

getBlogs(1234567); // error (too big)
getBlogs(1e100); // error (too big)

这只有在人们getBlogs()使用一些数字文字调用时才有效。数学运算往往会产生number,它太宽而无法验证:

getBlogs(3+1); // error: safe but the compiler can't tell

此外,我注意到像上面这样的大型联合使我的 IDE 变得迟缓,所以我真的只建议对多达数百个的数字执行此操作。


另一种可能性是制作getBlogs()一个通用函数,其中验证参数以确保它是一个正整数。我认为,您可以使用模板文字类型在 TypeScript 4.1 中完成此操作:

type AsPositiveWholeNumber<N extends number> =
  N extends 0 ? PositiveWholeNumber :
  number extends N ? PositiveWholeNumber :
  `${N}` extends `${infer F}${"." | "-"}${infer L}` ?
  PositiveWholeNumber : N

type PositiveWholeNumber = {
  ["Please choose a value that is a positive whole number"]: number
} & number

function getBlogs<N extends number>(limit?: AsPositiveWholeNumber<N>) {
  console.log((limit || 0).toFixed());
}

在这里,该getBlogs()函数接受一个limit类型的参数AsPositiveWholeNumber<N>,其中N是一个number- 约束类型参数,由传入的值推断limit。该AsPositiveWholeNumber<N>类型被设计为如果N是一个正整数,那么AsPositiveWholeNumber<N>将只是N。否则,它将是PositiveWholeNumber一个虚拟类型,其目的是为用户提供一个看似有用的错误消息。

重要的工作在里面完成AsPositiveWholeNumber<N>。首先我们确保N既不是number也不是0。然后我们通过模板文字类型将其转换为字符串`${N}`,然后确保此字符串版本不N包含"."or"-"字符。这应该主要将正整数与其他数字区分开来,至少在我们得到巨大的值之前,这些值是整数,但其字符串表示以带有小数点的指数表示法呈现(例如1.23456789e+23)。

让我们看看它是否有效:

getBlogs(100); // okay
getBlogs(1); // okay
getBlogs(1.5); // error!
// ----> ~~~
// Argument of type '1.5' is not assignable to 
// parameter of type 'PositiveWholeNumber | undefined'
getBlogs(0); // error!
// ----> ~
// Argument of type '0' is not assignable to 
// parameter of type 'PositiveWholeNumber | undefined'
getBlogs(-4);
// ----> ~~
// Argument of type '-4' is not assignable to 
// parameter of type 'PositiveWholeNumber | undefined'

getBlogs(1234567); // okay
getBlogs(1e100); // okay, but
getBlogs(11e98); // error: becomes 1.1e+99
getBlogs(3 + 1); // error: safe but the compiler can't tell

所以这也有效。它有同样的问题,您需要getBlogs()使用数字文字进行调用,并且任何number类型的参数都不会被接受。这个解决方案肯定比纯联合有更多奇怪的移动部件,所以我不确定我是否真的会在任何生产代码中推荐这个。但我确实想展示 TypeScript 当前与您期望的行为有多接近。


Playground 代码链接


推荐阅读