背景:當你接手一些老的前端項目的時候,有時候這個項目同時又js和ts,如何快速將一個老的基於React的項目快速轉換爲ts風格。前端
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
@connect(
(state, props) => {
const { app, global, info, list, curItem } = state
return {
curApp: list[props.id],
app,
curItem,
total: info.total,
name: global.name,
}
},
dispatch =>
bindActionCreators(
{
save,
update,
remove,
},
dispatch
)
)
export default class Test extends Component {
static propTypes = {
obj: PropTypes.object,
isBool: PropTypes.bool,
str: PropTypes.string,
arr: PropTypes.array,
oneOfType: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
node: PropTypes.node,
oneOf: PropTypes.oneOf(['a', 'b', 'c', 'd']),
func: PropTypes.func,
required: PropTypes.func.isRequired,
}
constructor(props) {
super(props)
this.state = {
isShowModal: false,
modalName: props.report_name || '',
modalType: 'save',
confirmLoading: false,
monitorModalVisible: false,
}
this.aaa = '111'
}
render() {
return <div>hello tscer</div>
}
}
複製代碼
import { CompilerOptions, createPrinter, createProgram, EmitHint, transform } from 'typescript'
const program = createProgram([realPath], compileOptions)
const sourceFiles = program
.getSourceFiles()
.filter(s => s.fileName === realPath)
const typeChecker = program.getTypeChecker()
const result = transform(sourceFiles, [
generateGenericPropAndState(typeChecker),
removeImportPropTypes(typeChecker),
removeStaticPropTypes(typeChecker),
])
const printer = createPrinter()
const printed = printer.printNode(
EmitHint.SourceFile,
result.transformed[0],
sourceFiles[0]
)
const res = prettier.format(printed, {
semi: true,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
parser: 'typescript',
})
複製代碼
createProgramnode
program.getSourceFiles()react
transformgit
createPrintergithub
import { isImportDeclaration, isStringLiteral, SourceFile, updateSourceFileNode } from 'typescript'
export const removeImportPropTypes = (typeChecker: TypeChecker) => (
context: TransformationContext
) => (sourceFile: SourceFile) => {
const statements = sourceFile.statements.filter(
s =>
!(
isImportDeclaration(s) &&
isStringLiteral(s.moduleSpecifier) &&
s.moduleSpecifier.text === 'prop-types'
)
)
return updateSourceFileNode(sourceFile, statements)
}
複製代碼
這裏更多的會將思考過程,代碼細節你們能夠本身去試試就知道了typescript
一個transform的高階方程express
sourceFile的結構, 這裏就用到我以前說的ast viewwejson
中間的SourceFile便是sourceFile的結構了,選擇代碼也能夠看到代碼對應的ast結構redux
這裏咱們須要把 import PropTypes from 'prop-types' 刪除掉,明細這裏對應的是一個叫作 ImportDeclaration 的結構小程序
sourceFile.statements 表明的是每個代碼塊
filter就按照我以上說的邏輯 去除掉 prop-types
最後返回 updateSourceFileNode, 生成了一個更新後咱們須要新的sourceFile返回
以後的transform功能相似於此的思考過程,因爲ts結構vscode有很好的代碼提示,以及類型註釋,一些ts compiler的api你們根據對應Node的定義應該能夠很快的適應
export const removeStaticPropTypes = (typeChecker: TypeChecker) => (
context: TransformationContext
) => (sourceFile: SourceFile) => {
const visitor = (node: Node) => {
if (isClassDeclaration(node) && isReactClassComponent(node, typeChecker)) {
return updateClassDeclaration(
node,
node.decorators,
node.modifiers,
node.name,
node.typeParameters,
createNodeArray(node.heritageClauses),
node.members.filter(m => {
if (
isPropertyDeclaration(m) &&
isStaticMember(m) &&
isPropTypesMember(m)
) {
// static and propTypes
return false
}
if (
isGetAccessorDeclaration(m) &&
isStaticMember(m) &&
isPropTypesMember(m)
) {
// static and propTypes
return false
}
return true
})
)
}
return node
}
return visitEachChild(sourceFile, visitor, context)
}
複製代碼