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 -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. 报错不是敌人,是在帮你提前发现问题
更多推荐


所有评论(0)