技术备忘 TypeScriptJavaScript类型系统

前言

TypeScript 是 JavaScript 的超集1TypeScript 是 JavaScript 的超集,意味着所有有效的 JavaScript 代码都是有效的 TypeScript 代码。TypeScript 添加了类型注解和编译时检查,最终编译为纯 JavaScript。,增加了静态类型系统2类型系统是编程语言用来检查和约束值类型的规则集合。静态类型系统(如 TypeScript)在编译时检查,动态类型系统(如 JavaScript)在运行时检查。。它不能让你少写代码,但能让你在编译时而不是运行时发现 bug,配合编辑器提示体验大幅提升。

如果你写过几万行 JS 然后被 undefined is not a function 折磨过,你会理解 TypeScript 的价值。

基础类型

ts
// 基本类型
let name: string = 'Alice'; // 类型推断[^type-inference]可省略: let name = 'Alice'
let age: number = 30;
let isActive: boolean = true;
let data: null = null;
let undef: undefined = undefined;
// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型写法
// 元组(固定长度和类型)
let tuple: [string, number] = ['Alice', 30];
// 枚举
enum Direction { Up, Down, Left, Right }
let dir: Direction = Direction.Up;
// any(尽量少用)
let loose: any = 'anything';
loose = 42;
类型说明何时使用
any关闭类型检查尽量不用,迁移 JS 时临时用
unknown类型安全版 any不确定类型时用,使用前需判断
void无返回值函数不返回任何值
never永远不会返回抛出异常或无限循环的函数
ts
// unknown 需要先判断再使用
function process(value: unknown) {
if (typeof value === 'string') {
console.log(value.toUpperCase());
}
}

接口与类型别名

Interface vs Type

对比interfacetype
扩展(继承)extends& intersection
合并声明✅ 同名自动合并❌ 不可重复声明
联合类型
工具类型(映射等)
实现implementsimplements
ts
// Interface(推荐用于对象定义)
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读
}
interface Admin extends User {
role: 'admin';
}
// Type(适合联合类型、工具类型)
type Status = 'active' | 'inactive' | 'pending';
type Point = { x: number; y: number };
type NamedPoint = Point & { name: string };
NOTE

优先用 interface 定义对象结构,用 type 定义联合类型、交叉类型、工具类型。这是 TypeScript 官方的推荐风格。

可选与只读

ts
interface Config {
url: string;
timeout?: number; // 可选:可以不传
readonly apiKey: string; // 只读:初始化后不可修改
}
const cfg: Config = { url: 'https://api.example.com', apiKey: 'sk-xxx' };
cfg.apiKey = 'new-key'; // ❌ 报错:只读

泛型(Generics)3泛型(Generics)允许在定义函数、接口或类时使用类型参数,在调用时再指定具体类型。它是类型系统的参数化多态机制,提高代码复用性。

泛型让类型像参数一样传递,是 TypeScript 复用能力的关键。

基础用法

ts
// 泛型函数
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const num = firstElement([1, 2, 3]); // number | undefined
const str = firstElement(['a', 'b']); // string | undefined
// 泛型接口
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
type UserResponse = ApiResponse<User>;
type ListResponse = ApiResponse<User[]>;
// 泛型约束
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
getLength('hello'); // ✅ string 有 length
getLength([1, 2, 3]); // ✅ array 有 length
getLength(42); // ❌ number 没有 length

常用泛型工具

ts
// Partial:所有属性变可选
type PartialUser = Partial<User>;
// Required:所有属性变必选
type RequiredUser = Required<User>;
// Pick:选取部分属性
type UserName = Pick<User, 'id' | 'name'>;
// Omit:排除部分属性
type UserWithoutEmail = Omit<User, 'email'>;
// Record:构造键值对类型
type PageMap = Record<string, number>;
// Exclude / Extract:联合类型过滤
type T = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
type T2 = Extract<'a' | 'b' | 'c', 'a'>; // 'a'

类型守卫与类型窄化4类型窄化(Type Narrowing)是 TypeScript 根据控制流分析缩小变量类型范围的机制。通过 typeof、instanceof、in 等判断,将联合类型逐步缩窄为具体类型。

ts
function print(value: string | number | null) {
if (value === null) {
console.log('null');
} else if (typeof value === 'string') {
console.log(value.toUpperCase()); // 窄化为 string
} else {
console.log(value.toFixed(2)); // 窄化为 number
}
}
窄化方式说明
typeof基本类型判断
instanceof类实例判断
in属性存在判断
=== null / === undefinednull/undefined 判断
用户自定义守卫value is Type 返回值
ts
// 自定义类型守卫
interface Cat { meow: () => void }
interface Dog { bark: () => void }
function isCat(pet: Cat | Dog): pet is Cat {
return (pet as Cat).meow !== undefined;
}
function handle(pet: Cat | Dog) {
if (isCat(pet)) {
pet.meow(); // ✅ 窄化为 Cat
}
}

类型断言

当你比 TypeScript 更清楚一个值的类型时使用:

ts
// as 断言
const input = document.getElementById('input') as HTMLInputElement;
input.value = 'hello';
// 非空断言(谨慎使用)
const name = maybeNull!.name;
// 双重断言(不推荐)
const num = (value as unknown) as number;
NOTE

类型断言是”告诉编译器你知道自己在做什么”,不会在运行时做任何检查。滥用 as any 等于放弃了类型检查的收益

函数

ts
// 函数参数和返回值
function add(a: number, b: number): number {
return a + b;
}
// 可选参数
function greet(name: string, greeting?: string): string {
return `${greeting ?? 'Hello'}, ${name}!`;
}
// 默认参数
function createUrl(path: string, base: string = 'https://example.com'): string {
return `${base}${path}`;
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
// 函数重载
function process(value: string): string[];
function process(value: number): number[];
function process(value: string | number): string[] | number[] {
if (typeof value === 'string') {
return value.split('');
}
return [value];
}

class

ts
class Animal {
// 可见性修饰符
public name: string; // 公开(默认)
private age: number; // 私有(仅本类)
protected type: string; // 受保护(本类及子类)
// 构造函数的参数自动变成属性
constructor(public id: number, name: string) {
this.name = name;
this.age = 0;
this.type = 'unknown';
}
// 方法
public speak(): void {
console.log(`${this.name} makes a sound`);
}
// getter / setter
get description(): string {
return `${this.name} (${this.age}岁)`;
}
// 静态属性
static kingdom = 'Animalia';
}

实用工具类型5工具类型是 TypeScript 内置的泛型类型,用于常见类型转换。如 Partial 将所有属性变可选,Pick<T, K> 选取部分属性,Omit<T, K> 排除部分属性。

TypeScript 内置了一系列工具类型,极大简化类型操作:

ts
// 属性修饰变化
interface Todo {
title: string;
description: string;
completed: boolean;
}
type PartialTodo = Partial<Todo>; // 全可选
type RequiredTodo = Required<Todo>; // 全必选
type ReadonlyTodo = Readonly<Todo>; // 全只读
// 属性选取
type Picked = Pick<Todo, 'title' | 'completed'>;
type Omitted = Omit<Todo, 'description'>;
// 提取和排除联合类型成员
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // 'a' | 'b'
// 获取函数返回值类型
type Result = ReturnType<typeof fetch>; // Promise<Response>
// 获取函数参数类型
type Params = Parameters<(a: string, b: number) => void>;
// [string, number]
// 获取 Promise 内部类型
type Data = Awaited<Promise<string>>; // string
// 构造键值对
type PageNames = Record<'home' | 'about' | 'contact', string>;

声明文件(.d.ts)6声明文件(.d.ts)描述 JavaScript 代码的类型信息,不包含实现。用于为纯 JS 库提供类型支持,或声明全局变量、模块类型。

ts
// globals.d.ts — 声明全局变量
declare const VERSION: string;
// modules.d.ts — 声明模块类型
declare module '*.svg' {
const content: string;
export default content;
}
declare module 'some-library' {
export function hello(): void;
export const version: string;
}

实际应用模式

类型安全的 API 请求

ts
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
async function fetchApi<T>(url: string): Promise<T> {
const res = await fetch(url);
const json: ApiResponse<T> = await res.json();
if (json.code !== 0) throw new Error(json.message);
return json.data;
}
// 使用
interface User { id: number; name: string }
const user = await fetchApi<User>('/api/user/1');
// user 的类型是 User

事件处理器映射

ts
type EventMap = {
click: { x: number; y: number };
keydown: { key: string };
focus: void;
};
function onEvent<E extends keyof EventMap>(
event: E,
handler: (data: EventMap[E]) => void
) {
// ...
}
onEvent('click', (data) => {
console.log(data.x, data.y); // ✅ 类型正确
});
onEvent('keydown', (data) => {
console.log(data.key); // ✅ 类型正确
});

条件类型

ts
// 根据条件返回不同的类型
type IsString<T> = T extends string ? 'yes' : 'no';
type A = IsString<string>; // 'yes'
type B = IsString<number>; // 'no'
// infer:提取类型
type Unpack<T> = T extends (infer U)[] ? U : T;
type C = Unpack<string[]>; // string
type D = Unpack<number>; // number

const 断言

ts
// 让字面量推导为确切值而非通用类型
const COLORS = {
primary: '#007bff',
secondary: '#6c757d',
} as const;
// 相当于:
// { readonly primary: '#007bff'; readonly secondary: '#6c757d' }
// 数组 const 断言
const roles = ['admin', 'user', 'guest'] as const;
type Role = (typeof roles)[number]; // 'admin' | 'user' | 'guest'

TypeScript 配置

json
{
"compilerOptions": {
"target": "ES2020", // 编译目标版本
"module": "ESNext", // 模块格式
"moduleResolution": "bundler", // 模块解析策略
"strict": true, // 启用全部严格检查(推荐)
"esModuleInterop": true, // 兼容 CommonJS 模块
"skipLibCheck": true, // 跳过 .d.ts 检查(加速)
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, // 允许 import JSON
"declaration": true, // 生成 .d.ts 文件
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
严格模式配置作用
strict: true开启下面所有严格检查
noImplicitAny不允许隐式 any
strictNullChecksnull/undefined 不能赋值给其他类型
strictFunctionTypes函数类型参数逆变检查
strictBindCallApplybind/call/apply 类型检查
noUnusedLocals不允许未使用的局部变量
noUnusedParameters不允许未使用的参数

学习路线图

  • 掌握基础类型(string/number/boolean/array/tuple/enum)
  • 理解 interface 和 type 的区别和用法
  • 学会泛型的基本使用
  • 掌握类型窄化和守卫
  • 熟练使用内置工具类型
  • 理解条件类型和 infer
  • 能写简单的声明文件
  • 了解类型体操(高级泛型编程)

总结

知识点要点
基础类型string/number/boolean/array/tuple/enum/unknown/never
接口与类型interface 定义对象,type 定义联合/交叉
泛型<T> 让类型可参数化,约束用 extends
窄化typeof / instanceof / in / 自定义守卫
工具类型Partial/Pick/Omit/Record/ReturnType/Awaited
声明文件.d.ts 描述 JS 库的类型
配置strict: true 是必选项
类型系统结构化类型系统7TypeScript 使用结构化类型系统(Structural Typing),也称鸭子类型。只要两个类型的结构兼容(属性和方法匹配),就可以互相赋值,不需要显式声明继承关系。(鸭子类型)

TypeScript 的学习曲线比 JavaScript 陡一些,但一旦习惯了类型提示就回不去了。从 strict: true 开始,配合编辑器的自动补全,适应期不会太长。


参考来源

脚注
  1. TypeScript 是 JavaScript 的超集,意味着所有有效的 JavaScript 代码都是有效的 TypeScript 代码。TypeScript 添加了类型注解和编译时检查,最终编译为纯 JavaScript。

  2. 类型系统是编程语言用来检查和约束值类型的规则集合。静态类型系统(如 TypeScript)在编译时检查,动态类型系统(如 JavaScript)在运行时检查。

  3. 泛型(Generics)允许在定义函数、接口或类时使用类型参数,在调用时再指定具体类型。它是类型系统的参数化多态机制,提高代码复用性。

  4. 类型窄化(Type Narrowing)是 TypeScript 根据控制流分析缩小变量类型范围的机制。通过 typeof、instanceof、in 等判断,将联合类型逐步缩窄为具体类型。

  5. 工具类型是 TypeScript 内置的泛型类型,用于常见类型转换。如 Partial 将所有属性变可选,Pick<T, K> 选取部分属性,Omit<T, K> 排除部分属性。

  6. 声明文件(.d.ts)描述 JavaScript 代码的类型信息,不包含实现。用于为纯 JS 库提供类型支持,或声明全局变量、模块类型。

  7. TypeScript 使用结构化类型系统(Structural Typing),也称鸭子类型。只要两个类型的结构兼容(属性和方法匹配),就可以互相赋值,不需要显式声明继承关系。