TypeScript快速入门:30分钟上手指南

北风里的开发者
2025-12-19 08:49
阅读 1671

上周五晚上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时间。

效果如何?

重构完钱包模块后,最爽的是:

  1. PR Review时间减半:同事不用再问“这个字段到底是什么类型?”
  2. 新人上手快:实习生看了类型定义就知道怎么用组件
  3. 自信多了:投简历时终于敢写“熟练使用TypeScript”

更重要的是,上周三我顺利试完婚纱回来,发现代码一次过CI,没出任何类型错误。那一刻,感觉TypeScript比婚纱还治愈(bushi)。

给求职者的真心话

如果你正在准备前端面试,TS几乎是必考项。但别死记硬背概念,重点展示:

  • 如何用TS提升代码健壮性(比如上面的钱包案例)
  • 如何渐进式迁移老项目
  • interface vs type、泛型等的理解(能说清适用场景就行)

我在最近一次面试中,就拿这个区块链钱包的例子讲了20分钟,面试官眼睛都亮了——毕竟不是每个人都能把婚庆和Web3扯到一起还不出错(手动狗头)。


最后送大家一句真理:TypeScript不会让你写出完美代码,但能让你少写出90%的愚蠢错误。

好了,我要去回婚礼策划师的消息了。希望这篇指南能帮你省下几个加班的夜晚——毕竟,程序员的时间,应该花在写代码和选喜糖上,而不是debug那些本可以避免的类型错误上。

Happy coding, and happy wedding planning! 💌

评论 0

最热最新
暂无评论
北风里的开发者Lv.1
0
影响力
0
文章
0
粉丝