TypeScript 零基础入门指南:从 JavaScript 到类型安全
一、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 -vnpm -v2. 全局安装 TypeScript 编译器npm install -g typescript验证安装tsc -v3. 创建第一个 TS 项目新建文件夹进入后执行npm init -ynpm install typescript --save-devnpx tsc --init这会生成 tsconfig.jsonTS 配置文件和项目结构。4. 编写并编译第一个文件创建 hello.tsconst 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: Arraystring [a, b]; // 等价写法3. 元组固定长度和类型的数组let person: [string, number] [张三, 25];// person[0] 是 stringperson[1] 是 number4. 枚举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[] 和 Arraynumber 等价团队内选一种统一即可四、类型推断TS 会自动帮你猜类型你不写类型TS 也会推断let count 10; // 推断为 numberlet 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、 判断、自定义类型守卫- 不要滥用联合类型超过 34 种组合时考虑用枚举或策略模式八、泛型Generics—— 写可复用的类型安全代码泛型像「类型的参数」让函数/类/接口适用于多种类型又不丢失类型信息。function identityT(value: T): T {return value;}const num identity(42); // T 推断为 numberconst str identity(hello); // T 推断为 stringinterface ApiResponseT {code: number;data: T;message: string;}type UserList ApiResponseUser[];【易错提醒】- 泛型不是 any编译后会被擦除不影响运行时- 不要为了炫技写三层嵌套泛型可读性会变差- 给泛型加约束 extends比无约束泛型更安全function getLengthT extends { length: number }(item: T) {return item.length;}九、类Classclass 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.tsexport function formatDate(d: Date): string {return d.toISOString();}export default class Logger {log(msg: string) { console.log(msg); }}// main.tsimport Logger, { formatDate } from ./utils;tsconfig.json 中常见配置module: ESNext 或 CommonJSmoduleResolution: 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 改成 TS1. 把 .js 重命名为 .ts简单文件2. 搞不定的复杂文件先改成 .js在 TS 里用 // ts-check JSDoc3. 安装 types/xxx 获得第三方库的类型npm install --save-dev types/nodenpm 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 用太多掩盖真实 bug10. 混淆 和 TS 不管这个但 JS 运行时会有坑11. 接口里写了方法实现类时漏写或签名不一致12. 枚举数字枚举和字符串枚举混用导致比较出错13. 修改了 TS 代码但忘记重新编译运行的还是旧 JS14. 在 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): PromiseArticle[] {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 可以先会 JS2. 类型是为了协作和防错不是为了写论文3. 报错不是敌人是在帮你提前发现问题