从 JS 转型到 TS 的实战指南:30 分钟上手 TypeScript

@李雨佳
2025-06-21 14:41
阅读 575

引言:为什么我会决定学 TypeScript?

引言:为什么我会决定学 TypeScript?

记得两年前,我加入一个中型前端团队,负责一个电商后台系统的重构。当时项目已经运行了两三年,代码库膨胀得很快,模块之间调用混乱、函数参数类型不明、接口返回结构随意更改,导致每次新功能上线都像是“拆弹”——你永远不知道哪一行代码会突然报错。

那时候我们还在使用纯 JavaScript(ES6+),虽然有 ESLint 和 JSDoc 帮忙约束代码质量,但远远不够。后来我们团队做了一个决定:全面引入 TypeScript。

刚开始我也有些抵触,觉得多了不少“冗余”的类型声明,写起来不够灵活。但在第一个迭代周期后,我就彻底改变了看法。TypeScript 给我们带来的不仅仅是类型安全,更是一种开发流程的“安全感”。

今天这篇文章,我想以自己真实的经验为基础,带大家在 30 分钟内快速上手 TypeScript,并结合我在实际项目中遇到的问题,分享一些经验教训和小技巧。


问题描述:JS 项目的“不可控状态”

问题描述:JS 项目的“不可控状态”

让我印象深刻的是那次重构订单管理页面的经历。我们原有的一套 API 接口封装,是基于 Axios 编写的几个工具函数,用来请求数据并渲染表格。但问题来了:

  • 后端返回的数据结构经常变更
  • 函数参数没有清晰定义,有时候传 string,有时候传 number
  • 表格组件内部对字段的处理方式依赖特定结构,一旦变更就会崩溃

这些情况在 JS 中几乎无法提前发现,只能靠手动测试或者上线之后 catch 错误,效率很低。

更严重的是,团队协作时经常出现:“我以为这个字段是 string”,“你怎么不传 id 啊?”。这些都是典型的类型模糊问题。


解决方案:为什么选择 TypeScript?

解决方案:为什么选择 TypeScript?

JavaScript框架对比-2

TypeScript 的核心优势在于 静态类型检查 + 开发提示(IntelliSense)

我们可以利用它的类型系统提前定义好变量、函数参数、返回值、接口结构等,在编写阶段就能发现潜在问题。更重要的是,它可以在不改变代码逻辑的前提下逐步迁移已有项目,不需要一次性全部重写。

于是我们决定:

  • 在新功能模块优先使用 .ts 文件
  • 已有 .js 文件添加 // @ts-check 来开启简单类型检测
  • 使用 VS Code 的智能提示和跳转功能提升开发效率
  • 配合 ESLint + Prettier 规范编码风格

入门实践:30分钟掌握 TypeScript 核心概念

JavaScript框架对比-1

入门实践:30分钟掌握 TypeScript 核心概念

以下内容是我根据自己学习路径整理出的最短上手路径,覆盖了绝大多数日常开发中需要用到的核心语法和类型知识。

环境搭建

npm install -g typescript

如果你是在现有项目中使用 TypeScript,建议使用构建工具(如 Webpack、Vite)内置的支持。

Vite 创建项目示例:

npm create vite@latest my-ts-app --template vanilla-ts

创建完成后你会看到 index.htmlmain.ts 还有关键的配置文件 tsconfig.json

我的小技巧:VSCode 安装官方插件 “TypeScript Toolkit”,可以自动补全很多类型定义和 import 引入。


第一步:基本类型声明

JavaScript 是动态类型语言,而 TypeScript 则鼓励我们显式地定义类型:

let age: number = 25;
let name: string = "Tom";
let isActive: boolean = true;

function greet(person: string): string {
  return `Hello, ${person}`;
}

greet(name); // 正确
greet(age);  // 报错!age 不是 string 类型

这样可以避免类似“向 greet 函数传数字”的错误。


第二步:接口(Interface)与类型别名(Type)

当我们要表示对象结构时,可以用 interfacetype

interface User {
  id: number;
  name: string;
  email?: string; // 可选属性
}

type Product = {
  productId: string;
  price: number;
};

注意:interface 支持继承,type 更适合联合类型。

比如我们在处理用户信息时,后端可能会返回两种情况:登录用户带有 token,未登录则是公共信息。

interface AuthenticatedUser extends User {
  token: string;
}

type UserInfo = User | AuthenticatedUser;

第三步:泛型与数组/函数类型

泛型能让我们写出更通用、复用性更强的代码:

function identity<T>(arg: T): T {
  return arg;
}

const output1 = identity<string>("hello"); // 输出为 string
const output2 = identity<number>(42);     // 输出为 number

数组的写法有两种:

const numbers: number[] = [1, 2, 3];
const names: Array<string> = ["Alice", "Bob"];

函数类型也可以写成接口:

interface SearchFunc {
  (source: string, subString: string): boolean;
}

第四步:可空类型与非空断言

这是我们从 JS 切换到 TS 最容易踩坑的地方之一:

let value: string | null = null;

if (value) {
  console.log(value.toUpperCase()); // 运行没问题,TS 也认可
}

console.log(value.toUpperCase()); // ❌ 会报错:Object is possibly 'null'.

解决办法有几种:

  • 提前判断是否为空
  • 使用非空断言操作符 !(慎用)
  • 使用默认值或条件赋值
const result = maybeGetResult()!;

不过非空断言很容易埋下隐患,我个人推荐尽量使用可选链操作符:

const title = data?.user?.profile?.title;

第五步:枚举与字面量类型

枚举常用于状态码、按钮类型、选项分类等场景:

enum Status {
  Pending,
  Approved,
  Rejected,
}

function checkStatus(status: Status) {
  if (status === Status.Approved) {
    // do something
  }
}

字面量类型则适用于更细粒度的控制:

type ButtonType = "primary" | "secondary" | "danger";

function renderButton(type: ButtonType) {
  // ...
}

第六步:类与装饰器(可选)

如果你的项目是面向对象风格,TS 对类的支持非常好:

class Person {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }

  sayHi() {
    console.log(`Hi, I'm ${this.name}`);
  }
}

const p = new Person("John");
p.sayHi();

装饰器(Decorator)常用于 Angular/Vue 等框架中进行元编程,但需要开启实验性支持(见 tsconfig.json 中设置 "experimentalDecorators": true)。


代码实践:订单详情页的重构

回到最初的那个订单管理页面,我们用 TypeScript 来重构一下它的核心部分。

假设后端返回数据格式如下:

{
  "orderId": "202308001",
  "customer": {
    "name": "张伟",
    "phone": "139****1234"
  },
  "products": [
    {
      "id": "P1001",
      "name": "无线蓝牙耳机",
      "price": 199.9
    }
  ],
  "status": "completed"
}

我们在 types.ts 定义对应的类型:

export type OrderStatus = "pending" | "processing" | "completed" | "cancelled";

export interface OrderProduct {
  id: string;
  name: string;
  price: number;
}

export interface Order {
  orderId: string;
  customer: {
    name: string;
    phone: string;
  };
  products: OrderProduct[];
  status: OrderStatus;
}

接着在请求方法里使用:

async function fetchOrder(orderId: string): Promise<Order> {
  const res = await fetch(`/api/orders/${orderId}`);
  return res.json();
}

这样只要后端接口变了,比如 product 没有 price 字段,我们的 TS 就会在编译时报错,而不是运行时报错!


踩坑经验:那些年我们掉进的“大坑”

坑一:第三方库没类型怎么办?

有时候我们会用一些老库,它们并没有 TypeScript 支持。这时候有两种办法:

  • 手动添加类型定义:declare module 'xxx'
  • 使用 DefinitelyTyped 社区维护的类型包(安装 @types/xxx

例如:

npm install --save-dev @types/lodash

如果你不确定某个库有没有类型支持,可以访问 https://definitelytyped.org 查看。


坑二:Vue 项目怎么配置?

如果是 Vue 2 项目,可以通过 vue-property-decorator 结合 TS 使用。而 Vue 3 + Vite 则原生支持 <script lang="ts">

<script setup lang="ts">
import { ref } from 'vue'

const count = ref<number>(0)
</script>

我们当时从 Vue 2 升级到 Vue 3 的过程也顺带完成了 TypeScript 化,效果非常好。


坑三:类型太复杂,推导不出来?

有时复杂的嵌套结构或者泛型嵌套太多会让 TS 无法推导出正确类型,这时候需要显式标注:

function getFirstItem<T>(arr: T[]): T {
  return arr[0]
}

const items = [1, 2, 3];
const first = getFirstItem<number>(items);

不要迷信“类型推导”,尤其是复杂场景下,显式标注反而更清晰。


效果总结:TypeScript 带来的收益

自从我们项目接入 TypeScript 以后,收获非常明显:

  • 团队协作变得更加顺畅,沟通成本降低
  • 新人上手更快,因为代码结构更清晰
  • 上线后错误明显减少,尤其是 API 相关的类型错误
  • IDE 提示更好用了,写代码体验大幅提升

而且我们还结合 CI 自动加上了 tsc --noEmit --watch,每次提交代码前都会做一次类型检查,确保基础问题不会流入生产环境。


经验分享:给新手的几点建议

✅ 1. 从 JS 开始渐进式迁移

TypeScript 并不是必须全量使用的,你可以先在 JS 文件顶部加 // @ts-check,然后逐步转换为 .ts 文件。

✅ 2. 善用 VS Code 插件

除了基础的 IntelliSense,我还推荐两个插件:

  • TypeScript Importer:自动根据路径和文件名补全导入语句
  • Path Intellisense:补全相对路径更高效

✅ 3. 多写类型注解,少用 any(尤其不能滥用 unknown)

很多人为了省事写 any 类型,这样就失去了 TS 的意义。建议用 unknown 替代,强制你在使用前做类型收窄。

✅ 4. 合理使用联合类型和类型守卫

比如我们有个组件接受不同的数据来源:

type Source = LocalDataSource | RemoteDataSource;

function fetchData(source: Source) {
  if ('localData' in source) {
    // 处理本地数据
  } else {
    // 请求远程接口
  }
}

✅ 5. 别怕“啰嗦”,多写类型让代码更易读

虽然初学时会觉得要写很多额外的类型声明,但这其实是投资未来的行为。长远来看,这种“啰嗦”会让你的代码更健壮。


写在最后:TypeScript 让我重新爱上写代码

说实话,刚接触 TypeScript 的时候我也觉得麻烦。但慢慢地我发现,它不仅帮我抓住了很多隐藏的 bug,更提升了我对代码质量的关注。

尤其是在大型项目中,有了类型系统的加持,你可以放心大胆地重构、扩展,而不用担心一个小小的改动就引发蝴蝶效应。

如果你现在正在犹豫要不要学 TypeScript,那我的建议是:越早越好。它已经成为现代前端工程化不可或缺的一部分。

希望这篇来自真实项目经验的文章,能帮你快速入门,并找到属于自己的编码节奏。

祝你 coding 愉快,少踩坑、多成就 😊


如有问题欢迎留言交流,我们一起成长 💬

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝