我想创建一个从类型定义中提取"泛型类型参数"的函数(作为纯字符串(。
它应该采用如下输入字符串:
Foo<Bar, Baz<Qux>>
并返回一个带有引用类型 + 泛型的对象,如下所示(当然不必采用这种确切格式,只要我可以检索所需的信息(:
{
"name": "Foo",
"generics": [
{
"name": "Bar",
"generics": []
},
{
"name": "Baz",
"generics": [
{
"name": "Qux",
"generics": []
}
]
}
]
}
我的猜测是将String.match
与 /<.*>/g
这样的正则表达式一起使用,用逗号作为分隔符拆分结果,并递归解析每个参数的泛型。但是,我觉得这太复杂了,而且我错过了更简单的方法。
最简单的方法是递归构建一个键映射结构,然后将其转换为树。
下面的keyMapToTree
函数使用名为 keyMapToTreeInner
的内部帮助程序函数。
console.log(keyMapToTree(parseAsKeyMap('Foo<Bar, Baz<Qux>>')));
function parseAsKeyMap(input, tree = {}) {
input = input.trim();
let startIndex = input.indexOf('<'),
endIndex = input.lastIndexOf('>');
if (startIndex !== -1 && endIndex === -1) {
throw new Error("Missing closing bracket '>' for " + input);
} else if (startIndex === -1 && endIndex !== -1) {
throw new Error("Missing opening bracket '<' for " + input);
} else if (startIndex !== -1 && endIndex !== -1) {
let head = input.substring(0, startIndex),
tail = input.substring(startIndex + 1, endIndex);
tree[head] = {};
tail.split(/s*,s*/).forEach(token => parseAsKeyMap(token, tree[head]));
} else {
tree[input] = {};
}
return tree;
}
function keyMapToTree(input) {
let keys = Object.keys(input);
if (keys.length !== 1) {
throw new Error('Object must be non-null and have only one key!');
}
let key = keys[0], node = { name: key, generics: [] };
keyMapToTreeInner(input[key], node.generics);
return node;
}
function keyMapToTreeInner(input, nodeArray) {
Object.keys(input).map(key => {
let node = { name: key, generics: [] };
keyMapToTreeInner(input[key], node.generics);
nodeArray.push(node)
});
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}
<!--
The initial key-map will look like this, so convert this structure to a tree.
{
"Foo": {
"Bar": {},
"Baz": {
"Qux": {}
}
}
}
-->
深受Polywhirl先生的回答的启发,我创建了以下实现:
(为清楚起见,带有打字稿类型注释(
type TypeInfo = { //the returned object format
name: string;
generics: TypeInfo[];
}
function parseGenerics(input: string): TypeInfo {
input = input.trim();
const startIndex = input.indexOf('<'),
endIndex = input.lastIndexOf('>');
if (startIndex !== -1 && endIndex === -1) {
throw new Error("Missing closing bracket '>' for " + input);
} else if (startIndex === -1 && endIndex !== -1) {
throw new Error("Missing opening bracket '<' for " + input);
} else if (startIndex === -1 && endIndex === -1) { //no generics
return {
name: input,
generics: []
};
} else {
const head = input.substring(0, startIndex),
tail = input.substring(startIndex + 1, endIndex);
return {
name: head,
generics: tail.split(/s*,s*/).map(parseGenerics)
};
}
}
使用 Foo<Bar, Baz<Qux>>
作为输入,这将导致:
{
"name": "Foo",
"generics": [
{
"name": "Bar",
"generics": []
},
{
"name": "Baz",
"generics": [
{
"name": "Qux",
"generics": []
}
]
}
]
}
我更喜欢这种实现而不是 Polywhirl 先生的实现,因为它会立即创建正确的数据格式,而不需要额外的转换步骤。这使得它(在我看来(是一个更干净、更精简的解决方案。
如果您是 Chrome 用户,则此代码可在控制台中工作:
// let inputString = "Foo<Bar, Baz<Qux<Some, Thing<Else<But, Not>, So<Is>>, Other>>>"
let inputString = "Foo<Bar, Baz<Qux>>"
const replacements = {}
let replacementIndex = 0
while (true) {
const replacement = (inputString.match(/[A-Z][a-z0-9]+<(([A-Z][a-z0-9]+)[,s]*)+>/) || [])[0]
if (replacement) {
const key = `Key${replacementIndex}`
replacementIndex++
replacements[key] = replacement
inputString = inputString.replace(replacement, key)
} else {
break
}
}
const resultJson = {}
const parseSentence = (text) => {
const [key, valuesStr] = text.replace(/>$/, '').split(/</)
const values = valuesStr.split(',').map((x) => x.trim())
return {
[key]: values,
}
}
Object.keys(replacements).forEach((key) => {
resultJson[key] = parseSentence(replacements[key])
})
while (true) {
let replacementsFound = false
Object.keys(resultJson).forEach((key) => {
Object.keys(resultJson[key]).forEach((name) => {
resultJson[key][name] = resultJson[key][name].map((value) => {
if (/^Key[d+]$/.test(value)) {
replacementsFound = true
return resultJson[value]
}
return value
})
})
})
if (!replacementsFound) {
break
}
}
const resultKey = `Key${replacementIndex - 1}`
const unpreparedResult = resultJson[resultKey]
const prepareResultJson = (json) => {
const name = Object.keys(json)[0]
const generics = []
json[name].forEach((generic) => {
if (typeof generic === 'string') {
generics.push({ name: generic, generics: [] })
} else {
generics.push(prepareResultJson(generic))
}
})
const result = {
name,
generics,
}
return result
}
const finalResult = prepareResultJson(unpreparedResult)
console.log(finalResult)
您也可以关注此网址:https://codepen.io/SergioBelevskij/pen/ZPdVyM