[英]Extract generic type parameters from a string
我想創建一個從類型定義(作為純字符串)中提取“泛型類型參數”的函數。
它應該采用這樣的輸入字符串:
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": {} } } } -->
如果您是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
受Polywhirl先生的回答的啟發,我創建了以下實現:
(為清晰起見,帶有Typescript類型注釋 )
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先生相比,我更喜歡這種實現方式,因為它可以立即創建正確的數據格式,而不需要其他轉換步驟。 (我認為)這使它成為一種更清潔,更精簡的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.