是否可以在 TypeScript 的类型或接口内设置条件

2024-01-09

在 TypeScript 中,我可以像这样设置类型:

type mode = 1 | 2 | 3 | 4 | 5;

执行此操作,将允许的模式值限制为数字 1、2、3、4、5。

我正在尝试弄清楚是否/如何可以在类型上设置条件,因此我可以将其替换为以下内容,而不是声明如上所述的值:

type mode >= 0 && mode <= 5

如果这在类型中不可能,是否可以使用接口。我只是希望根据条件检查值,而不是如上所述的多个文字值。


没有子类型number在 TypeScript 中,它与以下函数返回的值完全对应true:

function validMode(mode: number): boolean {
    return mode > 0 && mode <= 5;
}
console.log(validMode(1), validMode(5), 
  validMode(2.5), validMode(Math.PI)); // true, true, true, true
console.log(validMode(0), validMode(6), 
   validMode(-1), validMode(Infinity)); // false, false, false, false

请注意,像这样的数字2.5 and Math.PI被接受,因为 JavaScript 中的值typeof mode === "number"是双精度浮点数并且不限于整数。

为了支持这种类型,TypeScript 可能需要以下一个或两个缺失的功能:

  • 细化类型 https://en.wikipedia.org/wiki/Refinement_type,您可以在其中以谓词的形式对类型设置条件;有一个关于此功能的请求微软/TypeScript#7599 https://github.com/microsoft/TypeScript/issues/7599,但看起来那里没有任何工作正在进行。

  • 数值的数学运算文字类型 https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types,您实际上可以在其中编写谓词,例如ValidMode > 0编译器会理解这对应于true or false取决于文字类型ValidMode。有一个关于此功能的请求微软/TypeScript#26382 https://github.com/microsoft/TypeScript/issues/26382,但同样,我没有看到任何明显的工作来支持这一点。

所以一般来说是不可能的。


当然是原版mode您编写的类型不接受之间的任意数字1 and 5,但仅限整数值。更像这样的东西:

function validMode(mode: number): boolean {
  return Number.isInteger(mode) && mode > 0 && mode <= 5;
}
console.log(validMode(1), validMode(5)); // true, true
console.log(validMode(2.5), validMode(Math.PI)); // false, false

而且,如你所知,这样的类型can被表示,但仅作为明确的union https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types有限数量的数字文字:

type ValidMode = 1 | 2 | 3 | 4 | 5;

实际上,如果一个工会的成员人数不超过 25 人,那么它就会表现得合理;如果它有超过 25 个但少于 10,000 个成员,则在某些情况下它会表现得很奇怪(请参阅微软/TypeScript#40803 https://github.com/microsoft/TypeScript/issues/40803例如),如果它有超过 10,000 名成员,您将接近或通过一些关于工会规模的硬性限制(请参阅这个错误信息 https://github.com/microsoft/TypeScript/blob/e638af7560530a76439d0ce9e2c517090fe6a60a/src/compiler/diagnosticMessages.json#L2513).

TypeScript 缺少“范围类型”的概念,人们可以在其中轻松编写

type ValidMode = Range<1, 5>; // or would that be Range<1, 6>? Inclusive/exclusive ????‍♂️

有一个开放的功能请求,位于微软/TypeScript#15480 https://github.com/microsoft/TypeScript/issues/15480。有一些涉及递归或泛型的解决方法,甚至模板文字类型 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#template-literal-types,但没有一个既可以理解又表现良好。如果您查看链接的问题,您可以找到一些导致联合的递归实现,如果您愿意,这将不起作用Range<0, 1.0e+6>甚至Range<0, 50>。除了娱乐目的之外,我不建议使用模板文字类型来解析字符串。

所以目前来看,这也是不可能的。


完全不同的方法是接受编译器无法轻松且准确地表示您的类型。相反,我们想创建一个branded https://github.com/microsoft/TypeScript/wiki/FAQ#can-i-make-a-type-alias-nominal的亚型number called ValidMode。我们可以写一个类型保护函数 https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates对其执行运行时检查number输入,并确定该输入是否是ValidMode。如果是这样,我们会使用虚拟属性“标记”该号码,将其标记为有效。如果没有,我们就不管它。然后你可以编写只接受的函数ValidMode并且编译器将强制执行此操作:

type ValidMode = number & { __validMode: true };
function validMode(mode: number): mode is ValidMode {
  return Number.isInteger(mode) && mode > 0 && mode <= 5;
}
function onlyAcceptValidModes(mode: ValidMode) {
  console.log(mode.toFixed(2));
}

这使得使用起来有点困难ValidMode,因为编译器无法验证像这样的数字文字4匹配它:

onlyAcceptValidModes(4); // error, number is not assignable to ValidMode

相反,您需要执行运行时验证测试以使您的值获得批准:

const someNumber = Math.random() < 0.5 ? 4 : 8;
onlyAcceptValidModes(someNumber); // error again

if (validMode(someNumber)) {
  onlyAcceptValidModes(someNumber) // okay
}

通过这种方式,您可以在 TypeScript 中模拟细化类型。由于测试仅在运行时存在,因此它可以像您想要的那样复杂。


Playground 代码链接 https://www.typescriptlang.org/play?ts=4.2.3#code/HYQwtgpgzgDiDGEAEApeIA2AvJBvAUPkkqJLAsgPLDIFHFIBmArsPAC4CWA9sEgG6ZOAEwCy3YRAAUYCRABcJZmABGEAE4BKRSu7cMEEHwIMG6iO2bq+syUgB8SAAxIAZK6S3kAHgC8SAFYAbnpiAF9QpHheKH0IADoMbgBzKUEMEXFJKQBGTQAaASExOSkAgqKMkuyAJnjywvTM0tEQdgALeIAFAElNTSCkAHohpHZ1ZghC8cnpiamx+cjo4FiDRJS04qzpJwqm6ukANn3t0oBaPMaz7J7gRk5gTnYAT37BkaZMKAXGb9--oU-hgfoRwmCSOBoHBEEgACoAd24eEiLDYXF4lWa2S8imAyjUWh0egMRhRpmI5ks1iQADkCRp4pwoHd2BBkhoZHJNG4PF4HM5eZ45Eg-IEQqYIqYVmsEklUgcdrlTlUleUBsNRjMFtrljE4hsFTdpHUGljDlJWh1un0NZ9gT8gf9Iq8YMgAGrGpD+HJIAA+SBq-qQAGZgwAWYPBQgQiIQ0jQijw9rmWgul5upCe1Ui-z41QaNx4JAAfRLirkim1SDCEoYaI4PD4FZxlaUBaJwrszKzXpMFKpVj49I7TJZwDZHPUXMkPPcXeQjhc8-5YujktRrEbmN4GBeAEF4IgYOxs9joDOFL2c7PyRSZQb5Zf4uxuAAxTgADwgwikNXekRSgwu4HkeEAnmehxQFI4Z2qMGjqNw6iFPmhJID2wDcOwSAgFAUCcMkoAqAYYzIpBOwQsQMrYbEkAjmh-hWp06hGMI3BgFIPLeM49RIAA-EgkaKAAHHWxAgYex6nsa0G0RA9EaHBSAIUhOHJCAjyUehjBIFsN7SHJClaDy-amBJYEQTJUiGQyWiakg3AANYgC8gGxvgYRAA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否可以在 TypeScript 的类型或接口内设置条件 的相关文章

随机推荐