我有这个代码:
const tokens = [
{
re: /^(#.*)$/,
parse: function(line: string) {
const style = styles.header;
return {
content: line,
style,
};
}
},
{
re: /^(__)([^_]+)(__)/,
parse: function(_:string, g1:string, text:string, g2:string) {
return {
content: text
};
}
}
];
const createChunks = (text: string) => {
return text.split('\n').map((line: string) => {
const chunk_parser = tokens.find(({re}) => re.test(line));
if (chunk_parser) {
const { parse, re } = chunk_parser;
const m = line.match(re);
if (m) {
return parse(...m);
}
}
return {
content: line
};
});
}
问题是我无法将 String::match 的结果转换为数组,即使它是一个类似数组的对象。 NodeJS 甚至在控制台中将其显示为数组。我可以使用扩展运算符或 Array.from 将匹配结果隐藏到 JavaScript 中的数组,但 TypeScript 不允许这样做并抛出此错误:
扩展参数必须具有元组类型或传递给剩余参数。
使用传播时,并且:
“string[]”类型的参数不可分配给“string”类型的参数
使用Array.from时。
这是 NodeJS 的输出:
> '# foo'.match(/^(#.*)$/)
[ '# foo', '# foo', index: 0, input: '# foo', groups: undefined ]
> [...'# foo'.match(/^(#.*)$/)]
[ '# foo', '# foo' ]
RegExp
的match()
方法的返回类型是RegExpMatchArray | null
。 RegExpMatchArray
的定义看起来像
interface RegExpMatchArray extends Array<string> {
0: string;
index?: number;
input?: string;
}
所以它是
string[]
的子类型,具有一些额外的属性。重要的是,已知 RegExpMatchArray
在索引 string
处有一个已定义的 0
元素;也就是说,数组的第一个元素不能是未定义的。
但不幸的是,当你在 TypeScript 中 spread
RegExpMatchArray
时,它会像 string[]
一样对待它,并且会丢失 0
存在的附加信息。这是您问题的主要原因。
我想说这是 TypeScript 的设计限制,尽管在我搜索 GitHub 问题时,我并没有真正看到有人抱怨传播
RegExpMatchArray
,所以我没有该分类的权威来源。但无论如何,你都需要解决它。
如果您想在 TypeScript 中传播
RegExpMatchArray
并使其工作,则需要切换到使用 开放式元组类型 [string, ...string[]]
。但 TypeScript 并不认为 RegExpMatchArray
可以分配给该类型(本质上与它不可传播的原因相同),因此您需要使用 类型断言。
因此我可能会使用的方法是这样的:
interface Token {
re: RegExp;
parse(
line: string,
...matches: string[]
): { content: string, style?: string }
}
const tokens: Token[] = { ⋯ }
在这里,我让编译器知道可以使用任意正数的
parse()
参数来调用 string
方法。如果您不这样做,那么编译器将看到您个人的 parse()
实现,并在您将任意长度的数组传递给仅接受两个或三个输入的对象时发出抱怨。
然后我们可以使用必需的类型断言:
return parse(...(m as [string, ...string[]]));
如果您不想使用类型断言并放松类型安全性,您可以使用
RegExpMatchArray
键手动索引到 0
,然后对其余部分进行切片:
return parse(m[0], ...m.slice(1));
这具有运行时效果,但更“安全”,因为编译器可以知道
m
实际上确实有 0
索引。
无论哪种方式,您都应该能够继续。