一、TypeScript 是什么?为什么要学?

TypeScript(简称 TS)是 JavaScript 的超集,由微软开发。你可以把它理解为「带类型的 JavaScript」。

核心特点:
  - 所有合法的 JavaScript 代码,几乎都是合法的 TypeScript 代码
  - 在运行前就能发现很多错误(编译阶段报错,而不是等到用户使用时才崩溃)
  - 编辑器(如 VS Code)能提供更准确的代码提示和自动补全

适合谁学?
  - 已经会一点 JavaScript,想写更稳、更易维护的代码
  - 准备做 Vue、React、Angular 等现代前端框架开发
  - 想参与 Node.js 后端或全栈项目

一句话总结:TS = JS + 类型系统 + 更好的开发体验。

二、环境搭建(第一步必做)

1. 安装 Node.js
   前往 https://nodejs.org 下载 LTS 版本并安装。
   安装后在终端输入以下命令验证:
     node -v
     npm -v

2. 全局安装 TypeScript 编译器
     npm install -g typescript

   验证安装:
     tsc -v

3. 创建第一个 TS 项目

   新建文件夹,进入后执行:
     npm init -y
     npm install typescript --save-dev
     npx tsc --init

   这会生成 tsconfig.json(TS 配置文件)和项目结构。

4. 编写并编译第一个文件

   创建 hello.ts:
     const message: string = "Hello, TypeScript!";
     console.log(message);

   编译:
     tsc hello.ts

   会生成 hello.js,用 node hello.js 运行即可。

【易错提醒】
  - 不要直接双击 .ts 文件在浏览器运行,浏览器不认识 TS,必须先编译成 JS
  - 全局 tsc 和项目内 typescript 版本可能不一致,团队项目优先用 npx tsc 或 package.json 里的脚本
  - tsconfig.json 里 "strict": true 建议保持开启,虽然报错多,但长期收益大

三、基础类型(从零开始的核心)

TypeScript 的类型就像「标签」,告诉编译器这个变量应该是什么。

1. 基本类型

   let name: string = "张三";
   let age: number = 25;
   let isStudent: boolean = true;
   let nothing: null = null;
   let notDefined: undefined = undefined;

2. 数组

   let nums: number[] = [1, 2, 3];
   let names: Array<string> = ["a", "b"];  // 等价写法

3. 元组(固定长度和类型的数组)

   let person: [string, number] = ["张三", 25];
   // person[0] 是 string,person[1] 是 number

4. 枚举

   enum Color { Red, Green, Blue }
   let c: Color = Color.Red;

5. any(尽量不用)

   let data: any = "可以是任何类型";
   data = 123;  // 不会报错,但失去了 TS 的意义

6. unknown(比 any 更安全)

   let input: unknown = "hello";
   // 使用前必须先判断类型
   if (typeof input === "string") {
     console.log(input.toUpperCase());
   }

7. void(函数无返回值)

   function log(msg: string): void {
     console.log(msg);
   }

8. never(永远不会正常结束的函数)

   function throwError(msg: string): never {
     throw new Error(msg);
   }

【易错提醒】
  - number 包含整数和小数,没有 int/float 之分
  - null 和 undefined 在 strictNullChecks 下不能随意赋给其他类型
  - 能不用 any 就不用,any 会让 TS 退化成 JS
  - 数组写法 number[] 和 Array<number> 等价,团队内选一种统一即可

四、类型推断:TS 会自动帮你猜类型

你不写类型,TS 也会推断:

   let count = 10;        // 推断为 number
   let title = "博客";    // 推断为 string

函数返回值也会推断:

   function add(a: number, b: number) {
     return a + b;  // 推断返回 number
   }

何时必须手写类型?
  - 函数参数(TS 无法从调用处反推参数类型)
  - 变量初始值为 null/undefined 但后续会赋其他值
  - 对外暴露的 API、库、组件 props

【易错提醒】
  - 不要给所有变量都写类型,简单场景让 TS 推断即可,代码更简洁
  - 但函数参数一定要写类型,否则默认为 any(在 noImplicitAny 关闭时)

五、对象与接口(interface)

定义对象的结构:

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

   const user: User = {
     id: 1,
     name: "李四"
   };

只读属性:

   interface Point {
     readonly x: number;
     readonly y: number;
   }

类型别名(type)与 interface 类似:

   type ID = number | string;

   type UserType = {
     id: number;
     name: string;
   };

interface vs type 怎么选?
  - 描述对象形状:优先 interface(可扩展、合并声明)
  - 联合类型、交叉类型、元组:用 type

【易错提醒】
  - interface 可以重复声明并自动合并,type 不行
  - 可选属性 ? 不等于可以是 undefined 赋值给必填属性,要看 strictNullChecks
  - 不要用 any 敷衍复杂对象,应定义 interface

六、函数类型

   // 参数类型 + 返回值类型
   function greet(name: string): string {
     return `你好,${name}`;
   }

   // 可选参数(放在必选参数后面)
   function buildName(first: string, last?: string): string {
     return last ? `${first} ${last}` : first;
   }

   // 默认参数
   function multiply(a: number, b: number = 1): number {
     return a * b;
   }

   // 剩余参数
   function sum(...nums: number[]): number {
     return nums.reduce((a, b) => a + b, 0);
   }

函数类型写法:

   type MathFn = (a: number, b: number) => number;

   const add: MathFn = (a, b) => a + b;

【易错提醒】
  - 可选参数不能放在必选参数前面
  - 箭头函数 this 指向与 JS 相同,TS 不会帮你改 this 行为
  - 回调函数记得写参数类型,否则容易隐式 any

七、联合类型与交叉类型

联合类型(或):

   type Status = "pending" | "success" | "error";
   type ID = number | string;

   function printId(id: number | string) {
     if (typeof id === "string") {
       console.log(id.toUpperCase());
     } else {
       console.log(id.toFixed(2));
     }
   }

交叉类型(且):

   type Named = { name: string };
   type Aged = { age: number };
   type Person = Named & Aged;

   const p: Person = { name: "王五", age: 30 };

【易错提醒】
  - 使用联合类型前必须「缩窄类型」(type narrowing),否则 TS 不知道你用的是哪一种
  - 常用缩窄方式:typeof、instanceof、in、=== 判断、自定义类型守卫
  - 不要滥用联合类型,超过 3~4 种组合时考虑用枚举或策略模式

八、泛型(Generics)—— 写可复用的类型安全代码

泛型像「类型的参数」,让函数/类/接口适用于多种类型,又不丢失类型信息。

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

   const num = identity(42);      // T 推断为 number
   const str = identity("hello"); // T 推断为 string

   interface ApiResponse<T> {
     code: number;
     data: T;
     message: string;
   }

   type UserList = ApiResponse<User[]>;

【易错提醒】
  - 泛型不是 any,编译后会被擦除,不影响运行时
  - 不要为了炫技写三层嵌套泛型,可读性会变差
  - 给泛型加约束 extends,比无约束泛型更安全:
      function getLength<T extends { length: number }>(item: T) {
        return item.length;
      }

九、类(Class)

   class Animal {
     name: string;

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

     move(distance: number = 0): void {
       console.log(`${this.name} moved ${distance}m`);
     }
   }

   class Dog extends Animal {
     bark(): void {
       console.log("汪汪!");
     }
   }

访问修饰符:
  - public:默认,内外都可访问
  - private:仅类内部
  - protected:类内部和子类
  - readonly:只读

【易错提醒】
  - TS 的 class 编译后仍是 JS 的 class/prototype,不是 Java 那套
  - 字段要先声明或在 constructor 里赋值,strictPropertyInitialization 会检查
  - implements 只检查「形状」,不会继承实现;extends 才是继承

十、模块化(import / export)

   // utils.ts
   export function formatDate(d: Date): string {
     return d.toISOString();
   }

   export default class Logger {
     log(msg: string) { console.log(msg); }
   }

   // main.ts
   import Logger, { formatDate } from "./utils";

tsconfig.json 中常见配置:
  "module": "ESNext" 或 "CommonJS"
  "moduleResolution": "node"

【易错提醒】
  - default export 和 named export 不要混用搞混导入方式
  - 导入路径区分大小写(Linux 服务器上尤其容易踩坑)
  - 循环依赖会导致类型变成 any 或 undefined,设计模块时尽量避免

十一、tsconfig.json 关键配置说明

{
  "compilerOptions": {
    "target": "ES2020",           // 编译目标 JS 版本
    "module": "ESNext",           // 模块系统
    "strict": true,               // 开启所有严格检查(强烈推荐)
    "esModuleInterop": true,      // 兼容 CommonJS 默认导出
    "skipLibCheck": true,           // 跳过库文件类型检查,加快编译
    "outDir": "./dist",           // 输出目录
    "rootDir": "./src",           // 源码目录
    "jsx": "react-jsx"            // React 项目需要
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

【易错提醒】
  - 没有 include/exclude 时,可能编译了不该编译的文件
  - outDir 和 rootDir 不匹配会导致输出目录结构混乱
  - 从 JS 迁移时先设 "allowJs": true,再逐步改 .ts

十二、与 JavaScript 协作:渐进式迁移

你可以一点点把 JS 改成 TS:

1. 把 .js 重命名为 .ts(简单文件)
2. 搞不定的复杂文件先改成 .js,在 TS 里用 // @ts-check + JSDoc
3. 安装 @types/xxx 获得第三方库的类型

   npm install --save-dev @types/node
   npm install --save-dev @types/react

【易错提醒】
  - 没有类型的老旧库,可自建 types/xxx.d.ts 声明模块
  - 不要用 @ts-ignore 逃避错误,除非万不得已并写清原因
  - declare module "*.css" 这类声明是前端项目常见需求

十三、零基础最常见的 15 个易错点汇总

 1. 以为 TS 会在运行时做类型检查 —— 错!类型只在编译期,运行时不存在
 2. 滥用 any,导致 TS 形同虚设
 3. 不装 @types 包,第三方库一片红报错
 4. 对象字面量多写了属性,TS 会报「多余属性」错误(用变量中转可解决)
 5. 把 null 赋给 string/number 等类型(strictNullChecks 下报错)
 6. 数组 push 了错误类型的元素
 7. 异步函数忘记 await,得到的是 Promise 而不是实际值
 8. JSON.parse 返回 any,需要手动断言或校验
 9. 类型断言 as 用太多,掩盖真实 bug
10. 混淆 == 和 ===(TS 不管这个,但 JS 运行时会有坑)
11. 接口里写了方法,实现类时漏写或签名不一致
12. 枚举数字枚举和字符串枚举混用导致比较出错
13. 修改了 TS 代码但忘记重新编译,运行的还是旧 JS
14. 在 Vue/React 中 props 类型写错,运行时才发现
15. 把 TS 语法当 JS 在浏览器直接引用(必须通过构建工具或 tsc 编译)

十四、一段综合小示例

   interface Article {
     id: number;
     title: string;
     tags: string[];
     published?: boolean;
   }

   function filterPublished(articles: Article[]): Article[] {
     return articles.filter(a => a.published === true);
   }

   async function fetchArticles(url: string): Promise<Article[]> {
     const res = await fetch(url);
     if (!res.ok) {
       throw new Error(`请求失败: ${res.status}`);
     }
     const data: unknown = await res.json();
     // 生产环境应做运行时校验,这里简化
     return data as Article[];
   }

这段代码展示了:interface、函数类型、可选属性、数组、Promise 泛型、
async/await、unknown 与类型断言。零基础可先照抄运行,再逐行改类型观察报错。

结语

TypeScript 的学习曲线比 JavaScript 陡一点,但「编译器教你写代码」的体验,
会在项目变大时省下大量调试时间。

记住三句话:
  1. TS 是 JS 的超集,不会 TS 可以先会 JS
  2. 类型是为了协作和防错,不是为了写论文
  3. 报错不是敌人,是在帮你提前发现问题
 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐