簡體   English   中英

從字符串中提取泛型類型參數

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM