如何在 Typescript 中定义正则表达式匹配的字符串类型?

Posted

技术标签:

【中文标题】如何在 Typescript 中定义正则表达式匹配的字符串类型?【英文标题】:How to define a regex-matched string type in Typescript? 【发布时间】:2018-12-28 22:57:00 【问题描述】:

是否可以定义一个包含字符串格式信息的接口?举个例子:

interface timeMarkers
    markerTime: string[]        
;

一个例子是:


   markerTime: ["0:00","1:30", "1:48"]                   

我的问题:有没有办法为markerTime 定义类型,使得字符串值必须始终匹配这个正则表达式,而不是简单地声明为string[] 并从那里开始?

var reg = /[0-9]?[0-9]:[0-9][0-9]/;

【问题讨论】:

没有办法定义这样的类型。 GitHub 上有一个提案支持这一点,但目前似乎不是优先事项。我会搜索问题并在此处发布 github.com/Microsoft/TypeScript/issues/6579 @TitianCernicova-Dragomir 谢谢。如果你想把它作为答案,我会接受它 无论将来是否存在,您仍然希望在生成的 javascript 输出中使用它。 【参考方案1】:

没有办法定义这样的类型。 GitHub 上有一个 proposal 来支持这一点,但目前似乎不是优先事项。对其进行投票,也许团队可能会将其包含在未来的版本中。

编辑

从 4.1 开始,您可以定义一种类型来验证字符串,而无需实际定义所有选项:

type MarkerTime =`$number| ''$number:$number$number`

let a: MarkerTime = "0-00" // error
let b: MarkerTime = "0:00" // ok
let c: MarkerTime = "09:00" // ok

Playground Link

【讨论】:

让 x:MarkerTime = "99999:999999";也将有效,因为 $number 允许任何数字,而不仅仅是一位数字 "93.242:942.23" 也匹配这个。 Github 上有一个新线程:github.com/microsoft/TypeScript/issues/41160 type string 是新的any,接下来是number。明确:如果您不想要任何数字,请不要使用允许任何数字的类型。 谢谢,这是我经过大量不必要的搜索后找到的唯一有用的帖子。我只是想要一个以 () 结尾的字符串,天哪。【参考方案2】:

我现在也在寻找类似的功能!

我最终想到了这个: 通过设置一个更复杂的开发环境是否有可能让它运行?也许您可以使用文件观察器来触发 tsc 并查找 TypeError 事件来更新您的 *d.ts 文件。

我的意思是这样的:

export type superRegexType = 'type-1' | 'type-2' | '/type-/';

作为一个钩子(初步建议):

const onTypeError = (err: Error, nameOfTypeSuperRegexType: string) => 
  const myTypesFile = require('fs').readFileSync(`path/to/\*d.ts`) as string;
  const searchFor = `export type $nameOfTypeSuperRegexType =`;
  const getDefinition = (inMyTypesFile: string, searchFor: string) => 
    const typeDefinitionString = inMyTypesFile.split(searchFor)[0].split(';')[0] + ';';
    const stringsInThere = typeDefinitionString.split(' | ').map(_str => _str.trim());
    const myRegexStr = stringsInThere.pop();
    return 
      oldTypeDefinitionString: typeDefinitionString,
      stringsInThere,
      myRegexStr,
      myRegex: new RegExp(myRegexStr)
    ;
  ;
  const myTypeDefinition = getDefinition(myTypesFile, searchFor);

  const shouldDynamicallyAddType = myTypeDefinition.myRegex.exec(err.message);
  if (!shouldDynamicallyAddType) 
    console.log("this is a real TypeError");
    console.error(err);
    return;
   else 
    const indexInErrMessage = shouldDynamicallyAddType.index;
    const _endIdx = err.message.indexOf('\'');
    const typeToAdd = err.message.slice(indexInErrMessage, _endIdx).trim();
    myTypeDefinition.stringsInThere.push(typeToAdd);
    const updatedTypeDefinitionString = `$searchFor $myTypeDefinition.stringsInThere.join(' | ') $myTypeDefinition.myRegexStr;`;
    myTypesFile.replace(myTypeDefinition.oldTypeDefinitionString, updatedTypeDefinitionString);
    // --> save that new d.ts and return hopefully watch the lint-error disappearing
  

也许这种解决方案可以让您在编译时根据您的 RegEx 动态添加类型。

你怎么看?

【讨论】:

【参考方案3】:

在正则表达式类型可用于该语言之前,您现在可以在 TS 4.1 中使用 template literal types。

让我参考问题示例并说明一下,如何对称为Time 的时间受限的string 类型进行建模。为简化起见,Time 需要格式为 hh:mm 的字符串(例如 "23:59")。

第一步:定义HHMM类型

将以下代码粘贴到您的浏览器 Web 控制台中:
Array.from(length:24,(v,i)=> i).reduce((acc,cur)=> `$acc$cur === 0 ? "" : "|"'$String(cur).padStart(2, 0)'`, "type HH = ")
Array.from(length:60,(v,i)=> i).reduce((acc,cur)=> `$acc$cur === 0 ? "" : "|"'$String(cur).padStart(2, 0)'`, "type MM = ")
生成的结果,我们可以将其用作 TS 中的类型:
type HH = '00'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|...|'22'|'23'
type MM = '00'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|...|'58'|'59'

第二步:声明Time

type Time = `$HH:$MM`

就这么简单。

第 3 步:一些测试

const validTimes: Time[] = ["00:00","01:30", "23:59", "16:30"]
const invalidTimes: Time[] = ["30:00", "23:60", "0:61"] // all emit error

这是一个live code example,可以与Time一起玩。

【讨论】:

这在小例子上效果很好,但在较大的例子上会中断,因为打字稿会产生所有可能组合的联合 @NathanAdams 是的,你可以有一个maximum of 100.000 union constituents,可以通过this playground sample 验证。 对于看到此答案的任何人,第一部分不是代码的一部分,而是应该提前完成以将所有组合作为字符串,然后可以将其用作常量字符串,这当然是只有当您准备好在代码中包含数千个选项时才是一个解决方案... 不过没有必要这么复杂,请看下面我的回答。【参考方案4】:

基于@bela53 的答案但更简单,我们可以做一个非常简单的解决方案,类似于@Titian,但没有缺点:

type HourPrefix = '0'|'1'|'2';
type MinutePrefix = HourPrefix | '3'|'4'|'5';
type Digit = MinutePrefix |'6'|'7'|'8'|'9';

type Time = `$HourPrefix | ''$Digit:$MinutePrefix$Digit`

const validTimes: Time[] = ["00:00","01:30", "23:59", "16:30"]
const invalidTimes: Time[] = ["30:00", "23:60", "0:61"] // all emit error

【讨论】:

似乎"29:00" 会匹配这种类型 - 所以这不是 100% 正确?! 解决这个问题并不难@daniel-sc。 但它使它更复杂,在我看来,这使得@bela53 的原始答案更简单【参考方案5】:

警告:TypeScript 可以使用@bela53 方法处理的内容存在限制...

基于@bela53 的answer,我尝试了以下类型定义的 IPv4 地址,导致"TS2590: Expression produces a union type that is too complex to represent." 当我忽略 TypeScript 错误并尝试无论如何都要在它仍然有效的前提下构建(我最终需要杀死并重新启动 IntelliJ)。

type segment = '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'10'|'11'|'12'|'13'|'14'|'15'|'16'|'17'|'18'|'19'|'20'|'21'|'22'|'23'|'24'|'25'|'26'|'27'|'28'|'29'|'30'|'31'|'32'|'33'|'34'|'35'|'36'|'37'|'38'|'39'|'40'|'41'|'42'|'43'|'44'|'45'|'46'|'47'|'48'|'49'|'50'|'51'|'52'|'53'|'54'|'55'|'56'|'57'|'58'|'59'|'60'|'61'|'62'|'63'|'64'|'65'|'66'|'67'|'68'|'69'|'70'|'71'|'72'|'73'|'74'|'75'|'76'|'77'|'78'|'79'|'80'|'81'|'82'|'83'|'84'|'85'|'86'|'87'|'88'|'89'|'90'|'91'|'92'|'93'|'94'|'95'|'96'|'97'|'98'|'99'|'100'|'101'|'102'|'103'|'104'|'105'|'106'|'107'|'108'|'109'|'110'|'111'|'112'|'113'|'114'|'115'|'116'|'117'|'118'|'119'|'120'|'121'|'122'|'123'|'124'|'125'|'126'|'127'|'128'|'129'|'130'|'131'|'132'|'133'|'134'|'135'|'136'|'137'|'138'|'139'|'140'|'141'|'142'|'143'|'144'|'145'|'146'|'147'|'148'|'149'|'150'|'151'|'152'|'153'|'154'|'155'|'156'|'157'|'158'|'159'|'160'|'161'|'162'|'163'|'164'|'165'|'166'|'167'|'168'|'169'|'170'|'171'|'172'|'173'|'174'|'175'|'176'|'177'|'178'|'179'|'180'|'181'|'182'|'183'|'184'|'185'|'186'|'187'|'188'|'189'|'190'|'191'|'192'|'193'|'194'|'195'|'196'|'197'|'198'|'199'|'200'|'201'|'202'|'203'|'204'|'205'|'206'|'207'|'208'|'209'|'210'|'211'|'212'|'213'|'214'|'215'|'216'|'217'|'218'|'219'|'220'|'221'|'222'|'223'|'224'|'225'|'226'|'227'|'228'|'229'|'230'|'231'|'232'|'233'|'234'|'235'|'236'|'237'|'238'|'239'|'240'|'241'|'242'|'243'|'244'|'245'|'246'|'247'|'248'|'249'|'250'|'251'|'252'|'253'|'254'|'255';
export type ipAddress = `$segment.$segment.$segment.$segment`;

我不确定是否有任何解决方法。

【讨论】:

【参考方案6】:
type D1 = 0|1;
type D3 = D1|2|3;
type D5 = D3|4|5;
type D9 = D5|6|7|8|9;

type Hours = `$D9` | `$D1$D9` | `$2$D3`;
type Minutes = `$D5$D9`;
type Time = `$Hours:$Minutes`;

整合来自@bela53 和@yoel halb 的想法的紧凑型解决方案。

这个解决方案有 2039 个时间类型的枚举成员。

Ts Playground

【讨论】:

20-23小时的$2难道不是简单的2吗?比如:'2$D3' ?【参考方案7】:

我的 2 美分


type digit01 = '0' | '1';
type digit03 = digit01 | '2' | '3';
type digit05 = digit03 | '4' | '5';
type digit09 = digit05 | '6' | '7' | '8' | '9';

type minutes = `$digit05$digit09`;
type hour = `$digit01 | ''$digit09` | `2$digit03`;

type MarkerTime = `$hour:$minutes`;

const ok: Record<string, MarkerTime> = 
  a: '0:00',
  b: '09:00',
  c: '23:59',
;

const notOk: Record<string, MarkerTime> = 
  a: '0-00',
  b: '24:00',
  c: '93.242:942.23',
;

【讨论】:

以上是关于如何在 Typescript 中定义正则表达式匹配的字符串类型?的主要内容,如果未能解决你的问题,请参考以下文章

在正则表达式中如何定义变量?

vscode如何配置debug,python正则表达式如何匹配括号,关于python如何导入自定义模块

如果字符串完全符合定义,则匹配正则表达式

如何修改与 Python 中特定正则表达式匹配的文本?

求教正则表达式如何匹配

TypeScript 中的正则表达式