-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e043d68
commit 611937e
Showing
1 changed file
with
177 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>json2ts</title> | ||
<style> | ||
html, | ||
body { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
|
||
textarea { | ||
width: 40%; | ||
height: 70%; | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<textarea id="input"></textarea> | ||
<textarea id="output"></textarea> | ||
<br> | ||
<button id='convert'>Convert</button> | ||
<script> | ||
|
||
const simpleTypes = ['string', 'number', 'boolean'] | ||
|
||
/** | ||
* 生成tscode | ||
* @param { object | any[] } obj 对象或者数组 | ||
* @param {string} name 模块名 | ||
* @returns | ||
*/ | ||
function convert(obj, name) { | ||
|
||
if (obj instanceof Array) { | ||
// array | ||
let keys = [...new Set(obj.reduce((c, v) => c.concat(Object.keys(v)), []))] // every key been used | ||
// descibe keys | ||
let keysDesc = keys.reduce((res, k) => { | ||
res[k] = { | ||
types: new Set([]), | ||
optional: false | ||
} | ||
return res | ||
}, {}) | ||
// process optional && types | ||
obj.forEach(item => { | ||
if (typeof item !== 'object' || !item) return // ignore every simple type & arrays | ||
// once any item not includes some keys in all, make them optional | ||
keys.filter(k => !Object.keys(item).includes(k)).forEach(k => keysDesc[k].optional = true) | ||
// generate types | ||
Object.keys(item).forEach(k => { | ||
let t = convertSingle(item[k], name, k) | ||
keysDesc[k].types.add(t) | ||
}) | ||
}) | ||
genCode(`interface ${name} {`) // start code | ||
Object.keys(keysDesc).forEach(k => { | ||
let key = keysDesc[k].optional ? `${k}?` : k // get key with optional | ||
let type = [...keysDesc[k].types].join(' | ') // get types | ||
genCode(` ${key}: ${type};`) | ||
}) | ||
genCode(`}`) // end code | ||
|
||
} else { | ||
// object | ||
genCode(`interface ${name} {`) // start | ||
Object.keys(obj).forEach(k => { | ||
let t = convertSingle(obj[k], name, k) | ||
genCode(` ${k}: ${t};`) // every key types | ||
}) | ||
genCode('}\n') // end | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param {any[]} arr 数组 | ||
* @param {string} k 键名,只在array中有对象时会使用 | ||
* @param {string} name 模块名 | ||
* @returns 类型 | ||
*/ | ||
function convertArray(arr, name, k = '') { | ||
let item = arr[0] | ||
// string number boolean | ||
if (simpleTypes.includes(typeof item)) return typeof item | ||
// null | undefined | ||
if (!item) return 'any' | ||
// array | ||
if (item instanceof Array) { | ||
return convertArray(item, name, k) + '[]' | ||
} | ||
// object | ||
let objName = name + toCamalCase(k) | ||
// 去重 | ||
stack.every(e => e.name !== objName) && stack.push({ // process by convert | ||
name: objName, | ||
value: arr | ||
}) | ||
return objName | ||
} | ||
|
||
function convertSingle(item, name, k = '') { | ||
let t = typeof item | ||
// string number boolean | ||
if (simpleTypes.includes(t)) return t | ||
// undefined | ||
if (t === 'undefined') return 'undefined' | ||
// null | ||
if (t === 'object' && !item) return 'null' | ||
// array | ||
if (item instanceof Array) { | ||
let type = convertArray(item, name, k) | ||
return `${type}[]` | ||
} | ||
// object | ||
if (t === 'object') { | ||
let objName = name + toCamalCase(k) | ||
stack.every(e => e.name !== objName) && stack.push({ // process by convert | ||
name: objName, | ||
value: item | ||
}) | ||
return objName | ||
} | ||
} | ||
|
||
function toCamalCase(str) { | ||
return str.replace(/(^|[-_])+(\w)/g, (...args) => { | ||
return args[2].toUpperCase() | ||
}) | ||
} | ||
|
||
let stack = [] | ||
let res = '\n\n' | ||
function genCode(code) { | ||
res += code + '\n' | ||
} | ||
|
||
|
||
const input = document.getElementById('input') | ||
const output = document.getElementById('output') | ||
document.getElementById('convert').addEventListener('click', () => { | ||
let json = input.value | ||
stack = [] | ||
res = '\n\n' | ||
|
||
try { | ||
try { | ||
json = JSON.parse(json) | ||
} catch (err) { | ||
json = new Function(`return ${json}`)() | ||
} | ||
} catch (err) { | ||
alert(err) | ||
} | ||
stack.push({ | ||
name: 'tsModule', | ||
value: json | ||
}) | ||
try { | ||
for (let i = 0; i < stack.length; i++) { | ||
convert(stack[i].value, stack[i].name) | ||
} | ||
} catch (err) { | ||
alert('解析失败:', err) | ||
} | ||
output.value = res | ||
}) | ||
</script> | ||
</body> | ||
|
||
</html> |