如何在 Typescript 中定义正则表达式匹配的字符串类型?
How to define a regex-matched string type in Typescript?
是否可以定义一个接口,其中包含一些关于字符串格式的信息?举个例子:
interface timeMarkers{
markerTime: string[]
};
一个例子是:
{
markerTime: ["0:00","1:30", "1:48"]
}
我的问题:有没有一种方法可以定义 markerTime
的类型,使字符串值必须始终匹配此正则表达式,而不是将其简单地声明为 string[]
并从那里开始?
var reg = /[0-9]?[0-9]:[0-9][0-9]/;
无法定义这样的类型。 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
我现在也在寻找类似的功能!
最后我想到了这个:
设置稍微复杂一点的dev-environment不就可以得到这个运行吗?也许您可以使用 file-watcher 来触发 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 动态添加类型。
你怎么看?
在该语言可以使用正则表达式类型之前,您现在可以在 TS 4.1 中使用 template literal types。
让我参考问题示例并说明,如何为名为 Time
的时间受限 string
类型建模。 Time
需要格式为 hh:mm
的字符串(例如 "23:59"
),这里是为了简化。
第 1 步:定义 HH
和 MM
类型
将以下代码粘贴到您的浏览器 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'
第 2 步:声明 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
。
根据@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
警告:TypeScript 可以使用 @bela53 方法处理的内容有限...
基于 @bela53 的 我尝试了 IPv4 地址的以下类型定义,导致 "TS2590: Expression produces a union type that is too complex to represent."
此定义导致 IntelliJ 消耗大量还有很多 CPU 次我忽略了 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}`;
我不确定是否有任何解决方法。
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 and @yoel halb 的想法。
此解决方案有 2039 个 Time 类型的枚举成员。
我的 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',
};
是否可以定义一个接口,其中包含一些关于字符串格式的信息?举个例子:
interface timeMarkers{
markerTime: string[]
};
一个例子是:
{
markerTime: ["0:00","1:30", "1:48"]
}
我的问题:有没有一种方法可以定义 markerTime
的类型,使字符串值必须始终匹配此正则表达式,而不是将其简单地声明为 string[]
并从那里开始?
var reg = /[0-9]?[0-9]:[0-9][0-9]/;
无法定义这样的类型。 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
我现在也在寻找类似的功能!
最后我想到了这个: 设置稍微复杂一点的dev-environment不就可以得到这个运行吗?也许您可以使用 file-watcher 来触发 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 动态添加类型。
你怎么看?
在该语言可以使用正则表达式类型之前,您现在可以在 TS 4.1 中使用 template literal types。
让我参考问题示例并说明,如何为名为 Time
的时间受限 string
类型建模。 Time
需要格式为 hh:mm
的字符串(例如 "23:59"
),这里是为了简化。
第 1 步:定义 HH
和 MM
类型
将以下代码粘贴到您的浏览器 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'
第 2 步:声明 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
。
根据@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
警告:TypeScript 可以使用 @bela53 方法处理的内容有限...
基于 @bela53 的 "TS2590: Expression produces a union type that is too complex to represent."
此定义导致 IntelliJ 消耗大量还有很多 CPU 次我忽略了 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}`;
我不确定是否有任何解决方法。
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 and @yoel halb 的想法。
此解决方案有 2039 个 Time 类型的枚举成员。
我的 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',
};