打字稿参数中允许的最小数量

Ifa*_*uki 2 javascript typescript

我有一个类似的功能:

function getBlogs(limit?: number) { ... }
Run Code Online (Sandbox Code Playgroud)

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

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

jca*_*alz 6

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


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

// 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());
}
Run Code Online (Sandbox Code Playgroud)

这将起作用,因为编译器将看到这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 | ...
Run Code Online (Sandbox Code Playgroud)

当然,超出范围的任何内容都会失败:

getBlogs(1234567); // error (too big)
getBlogs(1e100); // error (too big)
Run Code Online (Sandbox Code Playgroud)

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

getBlogs(3+1); // error: safe but the compiler can't tell
Run Code Online (Sandbox Code Playgroud)

另外,我还注意到像上面这样的大型联合体的问题足以让我的 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());
}
Run Code Online (Sandbox Code Playgroud)

在这里,该getBlogs()函数接受limit类型 的参数AsPositiveWholeNumber<N>,其中N是由传入的number值推断的约束类型参数。该类型的设计使得 if是正整数,则将是。否则,它将是一个虚拟类型,其目的是向用户提供看似有用的错误消息。limitAsPositiveWholeNumber<N>NAsPositiveWholeNumber<N>NPositiveWholeNumber

重要的工作是在里面完成的AsPositiveWholeNumber<N>。首先我们确保既不Nnumber也不是0。然后我们通过模板文字类型将其转换为字符串`${N}`,然后确保该字符串版本N不包含".""-"字符。这应该主要将正整数与其他数字区分开来,至少在我们得到整数的值之前,其字符串表示形式以带小数点的指数表示法呈现(例如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
Run Code Online (Sandbox Code Playgroud)

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