Osome 是如何从 Javascript 和 Flow 迁移到 TypeScript 的
这个童话故事由来已久。人生中,你常常押错注,但不幸的是,你对此无能为力。你在做决定时,总是基于有限的信息(尤其是关于未来的信息),所以本质上你是在赌博。这就是生活:有时发生在你的个人生活中,有时发生在你的工作生活中。但真正重要的是你如何从你的赌注中学习。
其中一个决定就是押注 Flow。如果你还不知道 Flow 是什么,它是 Facebook 的 JavaScript 语言超集,为 JavaScript 带来了类型和类型系统。它的理念非常简单:用一种语法糖 Flow 编写代码,它会获取你的文件并构建一个扩展的抽象语法树 (AST),对其进行分析,如果一切正常,就将其转换为常规 JavaScript。
虽然我们在 2017/2018 年开始开发应用程序,当时 TypeScript 的工具、支持和文档不如现在强大,Flow 和 TypeScript 之间也没有明显的优胜者。最初开发它的开发者对 Flow 更为熟悉。转折点出现在 2019 年左右,当时 TypeScript 的发展速度更快,Flow 基本上被抛弃了,我们也决定在某个时候迁移到 TypeScript。Flow 用户将面临以下问题:
- 未记录的功能,只有深入研究 Facebook 的代码库或 Flow 源代码才能了解
- 编译过程缓慢,会损坏你的电脑
- 维护人员对于您的问题和困难提供的支持非常薄弱
- 放弃 npm 包的用户空间类型
这个清单还可以一直列下去,但不知过了多久,我们突然想到应该过渡到 JavaScript 的另一个超集。如果你问我为什么,原因有几个,可以分为两部分:
公司特定原因:
- 我们已经在后端使用了 TypeScript,并且具备所需的专业知识和经验。
- 我们已经实现了合约优先的后端编程。不同客户端的 SDK 会根据规范自动生成。
- 我们的前端很大一部分是基于后端 API 的 CRUD 类接口。因此,我们希望为前端使用具有强类型支持的 SDK。
基于常识的原因:
- 出色的文档、许多教程、书籍、视频和其他内容。
- 弱类型系统。它存在争议,因为它不像ML类型系统(Reason、Haskell、Ocaml),因此任何背景的人都可以轻松地理解它。
- 社区的广泛采用,您能想到的大多数软件包都具有出色的 TypeScript 支持。
它是如何开始的?
据我所知,迁移是通过添加 tsconfig.json 开始的:
{
// ...
"compilerOptions": {
"allowJs": true
}
}
这个 TypeScript 标志允许我们开始向我们的系统添加文件ts
。tsx
接下来,我们开始逐一启用 ts 规则(以下选项仅供参考 - 选择适合您代码库的选项)
但是我们已经有了 Flow,所以让我们修复它并添加到.flowconfig
module.name_mapper.extension='ts' -> 'empty/object'
module.name_mapper.extension='tsx' -> 'empty/object'
现在,您已准备好开始迁移。开始吧!等等,就这么简单?
我们最终到了哪里?
由于各种原因,我们最终陷入了困境。我们有大量使用 Flow 和 TypeScript 编写的文件。我们花了几个月的时间才意识到这样做不行。
这里的主要问题之一是我们没有计划应该从哪些文件开始,需要花费多少时间和容量,以及其他组织和工程问题的组合。
另一天,再试一次
在接受了我们的代码库健康状况不佳的事实后,我们最终决定再次尝试。
这又是图表发挥作用的时候了。看看这张图片:
任何代码库都不过是一张图。所以,思路很简单:从图的叶子节点开始迁移,然后向图的顶部移动。在我们的例子中,迁移的是样式组件和 Redux 层逻辑。
假设你有sum
一个函数,它没有任何依赖项:
function sum(val1, val2) {
return val1 + val2;
}
转换为 TypeScript 后,您将获得:
function sum(val1: number, val2: number) {
return val1 + val2;
}
例如,如果您有一个用 JavaScript 编写的简单组件,当您转换该特定组件时,您会立即获得 TypeScript 带来的好处。
// imagine we continue the migration and convert the file form jsx to tsx
import React from 'react'
// PROFIT! Here's the error
const MySimpleAdder = ({val1}) => <div>{sum("hey", "buddy")}</div>
// => Argument of type 'string' is not assignable to parameter of type 'number'.
因此,让我们为您当前的项目创建另一张图片。
第一步是安装dependency-cruiser。
使用 CLI
depcruise --init
或者.dependency-cruiser.js
手动添加配置。我给你一个示例,用于概述此类迁移。
module.exports = {
forbidden: [],
options: {
doNotFollow: {
path: 'node_modules',
dependencyTypes: ['npm', 'npm-dev', 'npm-optional', 'npm-peer', 'npm-bundled', 'npm-no-pkg'],
},
exclude: {
path: '^(coverage|src/img|src/scss|node_modules)',
dynamic: true,
},
includeOnly: '^(src|bin)',
tsPreCompilationDeps: true,
tsConfig: {
fileName: 'tsconfig.json',
},
webpackConfig: {
fileName: 'webpack.config.js',
},
enhancedResolveOptions: {
exportsFields: ['exports'],
conditionNames: ['import', 'require', 'node', 'default'],
},
reporterOptions: {
dot: {
collapsePattern: 'node_modules/[^/]+',
theme: {
graph: {
rankdir: 'TD',
},
modules: [
...modules,
],
},
},
archi: {
collapsePattern:
'^(src/library|src/styles|src/env|src/helper|src/api|src/[^/]+/[^/]+)',
},
},
},
};
工具
Flow 到 TS — 帮助生成从 Flow 到 TS 文件的原始迁移,并减少 monkey job。TS
迁移— 帮助将 JS 文件转换为 Typescript 文件。这里有一个很棒的介绍。
分析和测量
这并非我的主意,但我们想到了跟踪迁移进度。在这种情况下,游戏化就显得尤为重要,因为它可以为其他团队和利益相关者提供透明度,并有助于衡量迁移的成功程度。
让我们深入探讨一下这个主题的技术细节。我们刚刚手动更新了 Excel 表和 Bash 脚本,该脚本会在每次推送到主分支时计算 JavaScript/Typescript 文件的数量,并将结果发布到 Slack 频道。这是一个基本示例,您可以根据自己的需求进行配置:
#!/bin/bash
REMOVED_JS_FILES=$(git diff --diff-filter=D --name-only HEAD^..HEAD -- '.js' '.jsx')
REMOVED_JS_FILES_COUNT=$(git diff --diff-filter=D --name-only HEAD^..HEAD -- '.js' '.jsx'| wc -l)
echo $REMOVED_JS_FILES
echo "${REMOVED_JS_FILES_COUNT//[[:blank:]]/}"
if [ "${REMOVED_JS_FILES_COUNT//[[:blank:]]/}" == "0" ]; then
echo "No js files removed"
exit 0;
fi
JS_FILES_WITHOUT_TESTS=$(find ./src -name ".js" ! -wholename "tests" -print | wc -l)
JS_FILES_TESTS_ONLY=$(find ./src -name ".js" -wholename "tests" -print | wc -l)
TOTAL_JS_FILES=$(find ./src -name ".js" ! -wholename "*.snap" -print | wc -l)
JS_SUMMARY="Total js: ${TOTAL_JS_FILES//[[:blank:]]/}. ${JS_FILES_WITHOUT_TESTS//[[:blank:]]/} JS(X) files left (+${JS_FILES_TESTS_ONLY//[[:blank:]]/} tests files)"
PLOT_LINK="<excellink>"
echo $JS_SUMMARY
AUTHOR=$(git log -1 --pretty=format:'%an')
MESSAGE=":rocket: ULTIMATE KUDOS to :heart:$AUTHOR:heart: for removing Legacy JS Files from AGENT Repo: :clap::clap::clap:\n```$REMOVED_JS_FILES```"
echo $MESSAGE
echo $AUTHOR
SLACK_WEBHOOK_URL="YOUR_WEBHOOK_URL"
SLACK_CHANNEL="YOUR_SLACK_CHANNEL"
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$MESSAGE\n$JS_SUMMARY\n\n$PLOT_LINK\",\"channel\":\"$SLACK_CHANNEL\"}" $SLACK_WEBHOOK_URL
停止代码库的流失
迁移是一个过程,不可能一蹴而就。人都是很棒的,但同时也有习惯,会感到疲惫等等。这就是为什么当你开始迁移时,人们会倾向于按照旧的方式做事。所以,你需要找到一种方法,防止工程师继续使用 JavaScript 而不是 Typescript 时代码库的流失。你可以使用自动化工具来实现这一点。第一个选择是编写一个脚本,检查是否有新的 JavaScript 文件添加到你的代码库中。第二个选择是使用与测试覆盖率相同的思路,并将其用于类型。
有一个有用的工具可以满足您的需要。
设置阈值,添加 npm 脚本,即可开始:
"type-coverage": "typescript-coverage-report -s --threshold=88"
结果
今天,我们的代码库中没有 Flow 或 JS 代码,我们很高兴选择使用 TypeScript,并成功将超过 200k 个 JavaScript 文件迁移到 TypeScript。
欢迎随时提问、表达任何意见或顾虑,或讨论你的观点。分享、订阅,创造代码,而不是战争。❤️
如果您发现错误,我很乐意修复它或向您学习 - 请告诉我。
如果您想与我一起工作,您可以在这里申请,或者在Twitter或LinkedIn上给我发直接消息。
文章来源:https://dev.to/frolovdev/how-we-migrate-from-javascript-and-flow-to-typescript-at-osome-4661