Typescript中interface自動化生成API文檔詳解
前言
最近在搞react組件庫,這兩天搞定了使用ast(抽象語法樹)去把interface轉(zhuǎn)為對象或者數(shù)組,這些數(shù)據(jù)就可以渲染為react組件的table或者markdown的table,啥意思呢,舉個例子:
UI層面
以下是interface的demo,被轉(zhuǎn)化
export interface TdAffixProps {
/**
* 指定滾動的容器。數(shù)據(jù)類型為 String 時,會被當(dāng)作選擇器處理,進(jìn)行節(jié)點查詢。示例:'body' 或 () => document.body
* @default () => (() => window)
*/
container: any;
/**
* @desc 距離容器頂部達(dá)到指定距離后觸發(fā)固定
* @default 0
*/
offsetBottom?: number;
/**
* @desc 距離容器底部達(dá)到指定距離后觸發(fā)固定
* @default 0
*/
offsetTop?: number;
/**
* @desc 固釘定位層級,樣式默認(rèn)為 500
*/
zIndex?: number;
/**
* @desc 固定狀態(tài)發(fā)生變化時觸發(fā)
*/
onFixedChange?: (affixed: boolean, context: { top: number }) => void;
}轉(zhuǎn)化為類似:

數(shù)據(jù)層面
interface被轉(zhuǎn)化為數(shù)組,數(shù)組里的每一項如下,可以傳給table組件去渲染,當(dāng)然有人想渲染為markdown格式,那把下面的數(shù)組渲染為markdown的table就行了,沒啥難度。
{
"name": "TdAffixProps",
"data": [
{
"name": "container",
"type": "any",
"jsdoc": {
"kind": 24,
"description": "指定滾動的容器。數(shù)據(jù)類型為 String 時,會被當(dāng)作選擇器處理,進(jìn)行節(jié)點查詢。示例:'body' 或 () => document.body",
"tags": [
{
"kind": 25,
"tagName": "default",
"text": "() => (() => window)"
}
]
}
},
{
"name": "offsetBottom",
"type": "number",
"isOptionnal": "?",
"jsdoc": {
"kind": 24,
"description": "",
"tags": [
{
"kind": 25,
"tagName": "desc",
"text": "距離容器頂部達(dá)到指定距離后觸發(fā)固定"
},
{
"kind": 25,
"tagName": "default",
"text": "0"
}
]
}
},
{
"name": "offsetTop",
"type": "number",
"isOptionnal": "?",
"jsdoc": {
"kind": 24,
"description": "",
"tags": [
{
"kind": 25,
"tagName": "desc",
"text": "距離容器底部達(dá)到指定距離后觸發(fā)固定"
},
{
"kind": 25,
"tagName": "default",
"text": "0"
}
]
}
},
{
"name": "zIndex",
"type": "number",
"isOptionnal": "?",
"jsdoc": {
"kind": 24,
"description": "",
"tags": [
{
"kind": 25,
"tagName": "desc",
"text": "固釘定位層級,樣式默認(rèn)為 500"
}
]
}
},
{
"name": "onFixedChange",
"type": "(affixed: boolean, context: { top: number }) => void",
"isOptionnal": "?",
"jsdoc": {
"kind": 24,
"description": "",
"tags": [
{
"kind": 25,
"tagName": "desc",
"text": "固定狀態(tài)發(fā)生變化時觸發(fā)"
}
]
}
}
]
}我們需要的數(shù)據(jù)結(jié)構(gòu)
上面可以看到,我們需要的數(shù)據(jù)結(jié)構(gòu)是
{
name: xxx, // interface的名字,
data: [
{
name: xx, // interface里每一項的屬性名
type: xx, // interface里每一項的類型
isOptionnal: xx, // 是否是可選項
jsDoc: {} // 后面細(xì)說
}
]
}簡單解釋一下jsdoc格式
JSDoc是一種文檔生成工具,可以用來為JavaScript代碼生成API文檔。它使用特殊的注釋格式來描述代碼中的類型、函數(shù)、變量等的用途、參數(shù)、返回值等信息。
例如,你可以在JavaScript代碼中使用如下的注釋來描述一個函數(shù):
/**
* 描述文字
* @default 0
*/
function sum(x, y) {
return x + y;
}這段注釋會被解析為:
{
"kind": 24, // 忽略
"description": "描述文字",
"tags": [
{
"kind": 25, // 忽略
"tagName": "default",
"text": 0
}
]
}AST解析技術(shù)選擇
為什么放棄babel
最開始我只知道babel,因為用webpack多了,不太了解ast相關(guān)的前端庫,然后很正常的這樣使用了,發(fā)現(xiàn)了問題:
const parser = require("@babel/parser")
const traverse = require("@babel/traverse").default
const generate = require('@babel/generator').default
const fs = require("fs")
fs.readFile('./type.ts', { encoding: 'utf-8' }, function (err, data) {
if (err) throw err;
const result = [];
const ast = parser.parse(data, {
sourceType: "unambiguous",
plugins: ["typescript"]
});
traverse(ast, {
TSInterfaceDeclaration(path) {
path.traverse({
TSPropertySignature(path) {
console.log(path.node.key.name);
console.log(path.node.leadingComments?.[0]?.value);
},
});
}
});
});比如number這個類型在上述打印節(jié)點的時候的類型是TSNumberKeyword,但是我拿到TSNumberKeyword不是目的,我要number,這個咋辦,
你說簡單啊,做個映射
{
TSNumberKeyword: "number"
}好,我知道簡單的映射可以,但是還有function類型,我咋映射,我需要還原的嘛,然后我想到了直接用generator把類型片段還原,但是總感覺有點low。
其次,我沒法直接獲得jsdoc的類型,因為注釋本質(zhì)上就是字符串,然后自己去折騰為jsdoc格式。
所以我去看了一下arco cli里的轉(zhuǎn)換使用到了ts-morph這個庫,發(fā)現(xiàn)這個庫在我這個需求下,是非常適合的,接下來介紹。
順便提一句,我的實現(xiàn)比字節(jié)團(tuán)隊的arco cli要簡單非常非常多!
ts-morph
這個庫極大的緩解了不懂typescript繁瑣底層類型和方法的同學(xué),具體的方法和屬性真的也是挺多的。ts-morph是一個針對 Typescrpit/Javascript的AST處理庫,可用于瀏覽、修改TS/JS的AST。
關(guān)于ts-morph的詳細(xì)文檔,參見其官網(wǎng):ts-morph.com/。
下面是我實現(xiàn)的基本思路(可以把里面的函數(shù)抽取為中間件,這樣更好維護(hù),目前懶得改了,類型沒認(rèn)真寫,大家可以在我的基礎(chǔ)上自己封裝適合自己業(yè)務(wù)的東西,思路還是很清晰的),后續(xù)會把它抽成一個單獨的庫給自己的react組件庫使用。
以下代碼說白了就一個簡單函數(shù),arco官方的cli工具雖然代碼也就200行的樣子,但是復(fù)雜度比我這個高很多。
自動化生成代碼
import { Project } from "ts-morph";
const internalProject = new Project({
tsConfigFilePath: "./tsconfig.json",
});
const sourceFile = internalProject.getSourceFile("./type.ts");
const interfaces = sourceFile!.getInterfaces();
const result:any[] = [];
interfaces.forEach((inter_face)=>{
result.push({
name: '',
data: []
});
const index = result.length - 1;
result[index].name = inter_face.getName();
inter_face.getProperties().forEach((v) => {
result[index].data.push({
name: v.getName(),
type: v.getTypeNode()?.getText(),
isOptionnal: v.getQuestionTokenNode()?.getText(),
jsdoc:v.getJsDocs().map((jsDoc)=>{
return (jsDoc.getStructure())
})[0]
});
});
})
console.log(result);總結(jié)
到此這篇關(guān)于Typescript中interface自動化生成API文檔的文章就介紹到這了,更多相關(guān)Typescript自動化生成API文檔內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript實現(xiàn)帶節(jié)日和農(nóng)歷的日歷特效
這篇文章主要介紹了javascript實現(xiàn)帶節(jié)日和農(nóng)歷的日歷特效,效果十分棒,需要的朋友可以參考下2015-02-02
JavaScript實現(xiàn)獲取某個元素相鄰兄弟節(jié)點的prev與next方法
這篇文章主要介紹了JavaScript實現(xiàn)獲取某個元素相鄰兄弟節(jié)點的prev與next方法,涉及JavaScript基于函數(shù)的判定及調(diào)用previousSibling與nextSibling的相關(guān)技巧,需要的朋友可以參考下2016-01-01
javascript 折半查找字符在數(shù)組中的位置(有序列表)
折半查找字符在數(shù)組中的位置(有序列表),需要的朋友可以參考下。2010-12-12
使用JavaScript給圖片添加水印的實現(xiàn)方法封裝
圖片加水印是一種常見的圖像處理技術(shù),通常用于保護(hù)版權(quán)、防止盜用、增加圖片的識別度等多種場景,這篇文章主要給大家介紹了關(guān)于使用JavaScript給圖片添加水印的實現(xiàn)方法封裝,需要的朋友可以參考下2024-03-03
jJavaScript中toFixed()和正則表達(dá)式的坑
這篇文章主要介紹了jJavaScript中toFixed()和正則表達(dá)式的坑,toFixed方法可以把Number四舍五入為指定小數(shù)位數(shù)的數(shù)字,具體詳細(xì)內(nèi)容需要的小伙伴可以參考一下2022-04-04

