TypeScript快速入门:30分钟上手指南
上周五晚上11点,我正一边切React组件一边刷婚礼场地的备选名单,耳机里放着Lo-fi beats——是的,这就是当代程序媛的日常:左手写代码,右手挑婚纱。突然,钉钉弹出一条消息:“下周三前要把新钱包模块重构完,要用TypeScript,顺便对接链上数据。”发信人是我们产品经理,一个坚信“区块链+婚庆”能颠覆行业的理想主义者。
我当时就懵了。咱之前项目虽然用React,但一直是JavaScript裸奔。现在要上TypeScript?还要处理区块链地址、交易哈希这些类型复杂的玩意儿?更别提下周三就是我试婚纱的日子!但转念一想:最近在偷偷看求职机会,几乎所有大厂JD都写着“熟悉TypeScript优先”。行吧,为了跳槽简历好看点,也为了不被产品带进“Web3婚庆元宇宙”的坑里翻车,咬牙干!
为什么是现在?
其实团队早就在讨论TS迁移的事。去年双11期间,我们线上出了个史诗级Bug:用户充值后余额没更新,原因是某个API返回的balance字段从字符串变成了数字,而前端代码里没人做类型校验。测试没覆盖到,运维半夜打电话把我叫醒。那一刻我就知道:动态类型语言在大型项目里就是定时炸弹。
再加上现在搞区块链相关开发,地址(42位十六进制字符串)、交易哈希、gas费用……这些数据类型要是不严格定义,调试起来简直像在黑暗森林里找bug。所以这次重构,既是业务需求,也是技术债的清算。
从零开始:30分钟极速上车
别被“30分钟”吓到,我不是说你能成为TS专家,而是让你能跑起来、能改代码、能不被类型系统气哭。毕竟我当初第一次看到Property 'x' does not exist on type 'never'的时候,真的想砸键盘。
第一步:初始化项目(5分钟)
假设你已经有React项目(没有的话npx create-react-app my-ts-app --template typescript),或者像我一样,在现有JS项目里渐进式迁移。
# 在现有React项目根目录
npm install --save-dev typescript @types/react @types/react-dom
然后新建一个tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true, // 允许混用JS和TS
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true, // 开启严格模式!别怂
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
💡 小技巧:
allowJs: true是渐进式迁移的关键。你可以先把入口文件改成.tsx,其他JS文件慢慢改。
第二步:把JS组件变成TSX(10分钟)
来看个真实场景:我们有个WalletDisplay组件,展示用户区块链钱包地址和余额。
原来的JS版本(问题多多):
// WalletDisplay.js
export default function WalletDisplay({ address, balance }) {
return (
<div>
<p>Address: {address}</p>
<p>Balance: {balance} ETH</p>
</div>
);
}
问题在哪?没人知道address应该是字符串,balance可能是数字或字符串。如果后端突然返回{ addr: "0x...", bal: "1.2" },组件直接挂掉。
TS改造版:
// WalletDisplay.tsx
interface WalletProps {
address: string; // 区块链地址必须是string
balance: number; // 我们约定前端统一用number
}
export default function WalletDisplay({ address, balance }: WalletProps) {
// 类型安全!IDE还能自动提示
return (
<div className="wallet-card">
<p>Address: {address.substring(0, 6)}...{address.substring(address.length - 4)}</p>
<p>Balance: {balance.toFixed(4)} ETH</p>
</div>
);
}
看到没?只要定义一个interface,所有传入的props立刻有法可依。而且VS Code会高亮警告:如果你不小心传了个数字当address,它会红得像我的婚礼请柬。
第三步:处理区块链数据(10分钟)
区块链返回的数据往往很野。比如以太坊的eth_getBalance返回的是十六进制字符串(如"0x12a05f200"),需要转成十进制数字。这时候TS的类型守卫(Type Guard)就派上用场了。
// utils/chain.ts
export interface BlockchainResponse {
balanceHex: string; // e.g., "0x12a05f200"
address: string;
}
// 类型守卫函数:确保输入符合BlockchainResponse
export function isValidChainData(data: any): data is BlockchainResponse {
return (
typeof data === 'object' &&
typeof data.balanceHex === 'string' &&
data.balanceHex.startsWith('0x') &&
typeof data.address === 'string' &&
data.address.startsWith('0x')
);
}
// 转换函数
export const hexToDecimal = (hex: string): number => {
return parseInt(hex, 16) / 1e18; // 转为ETH单位
};
在组件里使用:
// 在useEffect或SWR/fetch中
const [walletData, setWalletData] = useState<{ address: string; balance: number } | null>(null);
useEffect(() => {
fetch('/api/wallet')
.then(res => res.json())
.then(data => {
if (isValidChainData(data)) {
setWalletData({
address: data.address,
balance: hexToDecimal(data.balanceHex)
});
} else {
// 处理错误:后端又改接口了?
console.error('Invalid chain data:', data);
}
});
}, []);
这样,即使后端哪天把balanceHex改成balance_hex,你的代码也不会静默失败,而是明确报错。再也不用半夜被运维电话吵醒!
第四步:搞定React Hooks(5分钟)
TS和Hooks简直是天作之合。比如自定义一个useWallet Hook:
// hooks/useWallet.ts
import { useState, useEffect } from 'react';
import { hexToDecimal, isValidChainData } from '../utils/chain';
interface WalletState {
address: string;
balance: number;
isLoading: boolean;
error: string | null;
}
export const useWallet = (userAddress: string) => {
const [state, setState] = useState<WalletState>({
address: userAddress,
balance: 0,
isLoading: true,
error: null
});
useEffect(() => {
// 模拟API调用
fetch(`/api/balance?addr=${userAddress}`)
.then(res => res.json())
.then(data => {
if (isValidChainData(data)) {
setState(prev => ({
...prev,
balance: hexToDecimal(data.balanceHex),
isLoading: false
}));
} else {
throw new Error('Invalid API response');
}
})
.catch(err => {
setState(prev => ({ ...prev, error: err.message, isLoading: false }));
});
}, [userAddress]);
return state;
};
现在任何组件用useWallet都能获得完整的类型推断:
const { balance, isLoading, error } = useWallet("0x...");
// VS Code知道balance是number,isLoading是boolean...
那些让我崩溃又重生的坑
any是魔鬼:刚开始为了快,到处写any,结果类型检查形同虚设。后来强制自己:宁可花10分钟查类型定义,也不用any。泛型恐惧症:看到
Promise<T>就头大。后来发现,多数场景用interface就够了,泛型留到写工具库时再深究。第三方库没类型:比如某些小众区块链SDK。解决方案:自己写
.d.ts声明文件,或者临时用// @ts-ignore(慎用!)。strict模式真香:一开始关掉
strict觉得世界清净,直到上线又出Bug。现在全开,虽然开发时多写几行代码,但省下无数debug时间。
效果如何?
重构完钱包模块后,最爽的是:
- PR Review时间减半:同事不用再问“这个字段到底是什么类型?”
- 新人上手快:实习生看了类型定义就知道怎么用组件
- 自信多了:投简历时终于敢写“熟练使用TypeScript”
更重要的是,上周三我顺利试完婚纱回来,发现代码一次过CI,没出任何类型错误。那一刻,感觉TypeScript比婚纱还治愈(bushi)。
给求职者的真心话
如果你正在准备前端面试,TS几乎是必考项。但别死记硬背概念,重点展示:
- 如何用TS提升代码健壮性(比如上面的钱包案例)
- 如何渐进式迁移老项目
- 对
interfacevstype、泛型等的理解(能说清适用场景就行)
我在最近一次面试中,就拿这个区块链钱包的例子讲了20分钟,面试官眼睛都亮了——毕竟不是每个人都能把婚庆和Web3扯到一起还不出错(手动狗头)。
最后送大家一句真理:TypeScript不会让你写出完美代码,但能让你少写出90%的愚蠢错误。
好了,我要去回婚礼策划师的消息了。希望这篇指南能帮你省下几个加班的夜晚——毕竟,程序员的时间,应该花在写代码和选喜糖上,而不是debug那些本可以避免的类型错误上。
Happy coding, and happy wedding planning! 💌

评论 0