15分钟学会 TypeScript,适用于讨厌 TypeScript 的 JavaScript 开发者
Source: Dev.to

它是带有意图、纪律和凭证的 JavaScript。
从本质上讲,TypeScript 只是 JavaScript 加上 类型 和 泛型 —— 没有别的,也没有魔法。它不引入新的运行时行为;它只是确保你在编写代码时不会对自己(或你的团队成员)说谎。
如果你已经会 JavaScript,这是最快理解 TypeScript 真正重要之处的方式。
JavaScript 与 TypeScript:关于类型的真相
TypeScript 并未引入新的原始数据类型。
你已经了解的所有类型仍然存在:
stringnumberbooleannullundefinedsymbolbigint
TypeScript 的职责不是发明——而是强制执行。
- JavaScript: “运行时再去弄清楚。”
- TypeScript: “在运行之前先证明它。”
方法相同 — 安全不同
所有你熟悉的方法仍然存在:
- Strings →
concat,slice,charAt - Arrays →
map,filter,reduce,find
不同之处在于 TypeScript 知道你可以调用哪些方法。
let nums: number[] = [1, 2, 3];
nums.map(n => n * 2); // ✅ OK
nums.map(n => n.toUpperCase()); // ❌ Error – caught at compile time
JavaScript 会让第二行在运行时出错;TypeScript 在编译时就拦截它。这就是全部价值所在。
类型注解 vs. 类型推断
您可以显式注解:
let name: string = "Ram";
但 TypeScript 更聪明:
let name = "Ram"; // inferred as `string`
TypeScript 会自动跟踪类型。这被称为 类型推断,这也是为什么在一个地方声明变量而在其他地方修改它们通常是个坏主意。
any 陷阱(不要这么做)
很多人使用 any 来“逃离” TypeScript,这违背了其初衷。
let value: any = 10;
value.toUpperCase(); // ✅ TS allows it, but runtime explodes
这就是 tsconfig.json 中存在 noImplicitAny 的原因。如果你到处使用 any,实际上就是在写普通的 JavaScript,只是多了几步。
函数:参数 以及 返回值同样重要
TypeScript 不仅仅关乎输入——输出同样重要。
function addTwo(num: number): number {
return num + 2;
}
如果没有声明返回类型,下面的代码仍然可以编译,但会是一个错误:
function addTwo(num: number) {
return "hello"; // ❌ Type error if return type is declared
}
TypeScript 让你可以锁定契约的双方。
特殊返回类型
void→ 函数仅执行副作用never→ 函数永不返回(抛出异常或崩溃)
function handleError(msg: string): void {
console.log(msg);
}
function crash(msg: string): never {
throw new Error(msg);
}
为什么返回类型在团队中很重要
一个类型明确的函数即使不看其实现也能讲清楚它的行为:
function getCourse(): { title: string; price: number } {
return { title: "TypeScript Mastery", price: 499 };
}
任何人都能立刻看出:
- 它返回什么
- 返回值的精确结构
- 哪些字段是必需的
这不仅仅是语法问题,更是团队沟通的关键。
类型别名:命名形状
类型别名让你为意图命名。
type User = {
name: string;
email: string;
isActive: boolean;
};
JavaScript 说 “trust me.”
TypeScript 说 “prove it.”
readonly、可选属性和联合类型
readonly
readonly _id: string;
你可以读取它,但不能修改它。
可选属性
creditCard?: number;
该属性可能存在也可能不存在;TypeScript 强制你进行检查。
联合类型
let id: number | string;
广泛用于:
- 基于角色的访问控制(RBAC)
- API 响应
- 条件流程
元组:受控的混沌
元组是有序的、带类型的数组:
let user: [string, number, boolean];
- 它们仅在 TypeScript 中存在——编译后的 JavaScript 并不在意。
push()仍然可用(但请谨慎使用)。- 仅在 顺序真正重要 时使用它们。
枚举:具名常量
const enum SeatChoice {
aisle = 10,
middle,
window
}
- 可读且可预测。
- 零运行时歧义,因为枚举在编译时内联。
接口 vs. 类型(简短版)
接口
- 可扩展。
- 与类配合良好——强制执行 OOP 合约。
interface IUser {
email: string;
startTrial(): string;
}
- 接口可以 重新打开(声明合并)。
- 类型不能重新打开。
类型
- 适用于联合、交叉以及原始别名。
- 不能合并,但适合一次性形状。
类、构造函数和访问修饰符
TypeScript 让面向对象编程不再那么痛苦:
class User {
constructor(
public email: string,
public name: string,
private userId: string
) {}
}
访问修饰符
| 修饰符 | 可见性 |
|---|---|
public | 任意位置 |
private | 仅限类内部 |
protected | 类内部 以及 其子类内部 |
这些修饰符帮助你在实际系统中避免“访问混乱”。
接口 + 类 = 结构完整性
接口(形状)
interface TakePhoto {
cameraMode: string;
filter: string;
}
类(行为)
class Camera implements TakePhoto {
cameraMode = "auto";
filter = "none";
snap() { /* … */ }
}
接口 定义 必须存在的内容;类 提供 实现。
这种模式在后端(以及前端)系统中具有极佳的可扩展性。
抽象类:意图而非实例化
abstract class TakePhoto {
abstract getSepia(): void;
}
- 不能直接实例化。
- 子类 必须 实现抽象成员,以确保所需的行为。
泛型:真正的强力技巧
泛型让你一次性安全地编写逻辑:
function identity<T>(val: T): T {
return val;
}
泛型大显身手的常见场景:
- API 客户端包装器
- 仓储模式
- 响应模型
它们不是面向对象的替代品;而是类型安全性的进化。
类型收窄:运行时安全
TypeScript 从不“猜测”——你需要显式地收窄类型,使用:
typeof检查instanceof检查in运算符
这些技术让编译器满意,同时确保运行时行为正确,无需反射。
区分联合(最简模式)
interface CreditCard {
paymentType: "card";
cardNumber: string;
}
interface PayPal {
paymentType: "paypal";
email: string;
}
type PaymentMethod = CreditCard | PayPal;
- 单一的判别字段 (
paymentType) 零歧义。 - 非常适合
switch语句或穷尽的if检查。
结论
- TypeScript 并不会拖慢你的速度——不清晰的代码会。
- 它使歧义 非法,迫使你编写明确、可维护且类型安全的代码。