Skip to content
0

Ts-morph

解析 TS,常用于生成 d.ts 文件

获取跨文件 interface

借助 ts-morph 提供的 AST 分析工具,进行语法解析,找到我们想要的 Animal 类型

下面的例子引用了一个 @virtual/modules,确保在 tsconfig.json 中配置好了对应的 path 映射

// 目标:由当前文件为入口:
import type { Animal } from '@virtual/modules/exports.ts'

export const cat: Animal = {
  name: 'cat',
  age: 3,
}

// 得到 Animal 的类型,即 [ 'name: string', 'age: number' ]
export * from './typing.ts'
export interface Animal {
  name: string
  age: number
}
{
  "compilerOptions": {
    "baseUrl": ".",
    "module": "ESNext",
    "target": "ESNext",
    "lib": ["DOM", "ESNext"],
    "strict": true,
    "esModuleInterop": true,
    "jsx": "preserve",
    "skipLibCheck": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noUnusedLocals": true,
    "strictNullChecks": true,
    "allowJs": true,
    "forceConsistentCasingInFileNames": true,
    "paths": {
      "~/*": ["src/*"],
      "@virtual/modules/*": ["src/*"],
    }
  },
  "include": ["./*.ts", "src/*.ts", "src/*.d.ts", "src/*.tsx", "src/*.vue"],
  "exclude": ["dist", "node_modules", "example"]
}

我们的代码如下,查找到我们需要的 Identifier ,即 Animal

// src/morph.ts
import { ,  } from 'ts-morph'

const  = new ({
  : '../tsconfig.json',
})

const  = .('./index.ts')

通过 Kind,直接找到目标元素
const = .(.) .(.) .(.) .(.) .(.)
当然,还有更简单的方式:从后代中找
const = .(.) .(.()) //=> Animal

获得类型引用文本:

console.log(
    typeRef.getType()
    .getApparentProperties()
    .map(it => 
        it.getValueDeclarationOrThrow().getText()
    )
)
//=> [ 'name: string', 'age: number' ]

获取跨文件 Enum 映射

// 目标:由当前文件为入口
import { StyleKey } from '@virtual/modules/exports'

defineStyles({
  [StyleKey.WIDTH]: {
    title: '',
    default: 100,
  },
  [StyleKey.HEIGHT]: {
    title: '',
    default: 50,
  },
  [StyleKey.BOLD]: {
    title: '',
    default: false,
  },
  [StyleKey.BACKGROUND_COLOR]: {
    title: '',
    default: 'rgba(0, 0, 0, 0.3)',
  },
})

/**
得到以 StyleKey 映射的名字为 key,default 的值的类型为 value,即
[
  [ 'width', 'number' ],
  [ 'height', 'number' ],
  [ 'bold', 'boolean' ],
  [ 'background-color', 'string' ]
]
 */
export * from './typing.ts'
export enum StyleKey {
  'BACKGROUND' = 'background',
  'BORDER_COLOR' = 'border-color',
  'FONT_SIZE' = 'font-size',
  'PADDING' = 'padding',
  'WIDTH' = 'width',
  'BOLD' = 'bold',
  'HEIGHT' = 'height',
  'BACKGROUND_COLOR' = 'background-color',
  'COLOR' = 'color',
}
{
  "compilerOptions": {
    "baseUrl": ".",
    "module": "ESNext",
    "target": "ESNext",
    "lib": ["DOM", "ESNext"],
    "strict": true,
    "esModuleInterop": true,
    "jsx": "preserve",
    "skipLibCheck": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noUnusedLocals": true,
    "strictNullChecks": true,
    "allowJs": true,
    "forceConsistentCasingInFileNames": true,
    "paths": {
      "~/*": ["src/*"],
      "@virtual/modules/*": ["src/*"],
    }
  },
  "include": ["./*.ts", "src/*.ts", "src/*.d.ts", "src/*.tsx", "src/*.vue"],
  "exclude": ["dist", "node_modules", "example"]
}

我们的文件:

// src/morph.ts
import { Project, SyntaxKind } from 'ts-morph'

const project = new Project({
  tsConfigFilePath: '../tsconfig.json',
})

const file = project.getSourceFileOrThrow('./index.ts')

const arr: [string, string][] = []

const expressions = file.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression)

expressions.forEachChild((child) => {
  // getName
  const name = child
    .getFirstDescendantByKindOrThrow(SyntaxKind.PropertyAccessExpression)
    .getType()
    .getLiteralValue()
  const PropertyAssignment = child.getFirstDescendantByKindOrThrow(SyntaxKind.ObjectLiteralExpression).getLastChildByKindOrThrow(SyntaxKind.PropertyAssignment)

  // getValue
  const valueType = (() => {
    if (PropertyAssignment.getLastChildIfKind(SyntaxKind.NumericLiteral))
      return 'number'

    else if (PropertyAssignment.getLastChildIfKind(SyntaxKind.FalseKeyword) || PropertyAssignment.getLastChildIfKind(SyntaxKind.TrueKeyword))
      return 'boolean'

    else if (PropertyAssignment.getLastChildIfKind(SyntaxKind.StringLiteral))

      return 'string'
  })()

  arr.push([name, valueType])
})

console.log(arr)
/**
[
  [ 'width', 'number' ],
  [ 'height', 'number' ],
  [ 'bold', 'boolean' ],
  [ 'background-color', 'string' ]
]
 */

Released under the MIT License.