📚 下篇:高级特性
- 类型转换与类型断言
- 类型守卫(typeof, instanceof, in, 自定义守卫)
- 高级类型特性(条件类型、映射类型、模板字面量类型、索引类型)
- 内置工具类型大全(Partial, Pick, Omit, Record 等)
- 类型声明文件(.d.ts)
- 最佳实践和编码规范
- 学习路线建议
📚 目录
- 类型转换与类型断言
- 类型守卫
- 高级类型特性
- 类型声明文件
- 最佳实践
- 学习路线与建议
1. 类型转换与类型断言
1.1 类型转换方法
typescript
// 内置转换函数
let strNum: string = "123";
let num: number = Number(strNum);
let parsed: number = parseInt(strNum);
let float: number = parseFloat("3.14");
let numStr: number = 42;
let str: string = String(numStr);
let truthy: any = "hello";
let bool: boolean = Boolean(truthy);
1.2 类型断言
类型断言允许你告诉编译器具体是什么类型。
typescript
// 场景 1:已知更具体的类型
let rootElement: any = document.getElementById("app");
let htmlElement: HTMLElement = rootElement as HTMLElement;
let divElement: HTMLDivElement = rootElement as HTMLDivElement;
// 场景 2:联合类型 narrowing
function getLength(value: string | string[]) {
if ((value as string).length) {
return (value as string).length;
} else {
return (value as string[]).length;
}
}
1.3 两种断言语法
typescript
let someValue: any = "this is a string";
// 方式 1:as 语法(推荐)
let strLength: number = (someValue as string).length;
// 方式 2:尖括号语法
let strLength2: number = (<string>someValue).length;
2. 类型守卫
类型守卫是在运行时检查类型的方法。
2.1 typeof 守卫
typescript
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error("Invalid padding");
}
// 实际应用
function process(value: string | number) {
if (typeof value === "string") {
// 在这里 value 被推断为 string
console.log(value.toUpperCase());
} else {
// 在这里 value 被推断为 number
console.log(value.toFixed(2));
}
}
2.2 instanceof 守卫
typescript
class Bird {
fly() { console.log("flying"); }
layEggs() { console.log("laying eggs"); }
}
class Fish {
swim() { console.log("swimming"); }
layEggs() { console.log("laying eggs"); }
}
function move(pet: Bird | Fish) {
if (pet instanceof Fish) {
pet.swim();
} else {
pet.fly();
}
}
// 实际案例
class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
2.3 in 守卫
typescript
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function move2(pet: Bird | Fish) {
if ("swim" in pet) {
pet.swim();
} else {
pet.fly();
}
}
2.4 自定义类型守卫
typescript
// 使用类型谓词
function isFish(pet: Bird | Fish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// 使用守卫函数
if (isFish(animal)) {
animal.swim(); // animal 被推断为 Fish 类型
}
3. 高级类型特性
3.1 条件类型(Conditional Types)
typescript
// 根据条件选择类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实际应用:提取返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
function fn(): string {
return "hello";
}
type FnReturn = ReturnType<typeof fn>; // string
3.2 映射类型(Mapped Types)
typescript
// 基于现有类型创建新类型
// Readonly - 将所有属性设为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
// Partial - 将所有属性设为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
type PartialPerson = Partial<Person>;
// Pick - 选择特定属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type NameOnly = Pick<Person, "name">;
// Omit - 省略特定属性
type Omit<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P];
};
type AgeOnly = Omit<Person, "name">;
3.3 模板字面量类型
typescript
// 使用模板字符串定义类型
type Greeting = `Hello, ${string}!`;
let greeting1: Greeting = "Hello, Alice!";
let greeting2: Greeting = "Hello, Bob!";
// 组合使用
type EventName = "click" | "hover" | "focus";
type Handler = `${EventName}Handler`;
type ClickHandler = "clickHandler"; // ✅
type HoverHandler = "hoverHandler"; // ✅
3.4 索引类型(Index Types)
typescript
// 索引类型查询
interface Person {
name: string;
age: number;
city: string;
}
// 获取属性的类型
type PersonName = Person["name"]; // string
type PersonAge = Person["age"]; // number
// 获取所有属性的类型
type PersonKeys = keyof Person; // "name" | "age" | "city"
type PersonValues = Person[keyof Person]; // string | number
// 实际应用
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let person = { name: "Alice", age: 30 };
let name = getProperty(person, "name"); // string
3.5 工具类型(Utility Types)
TypeScript 内置了常用的工具类型:
typescript
// Partial<T> - 将 T 的所有属性设为可选
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
// ...
}
// Required<T> - 将 T 的所有属性设为必需
type RequiredTodo = Required<Todo>;
// Readonly<T> - 将 T 的所有属性设为只读
type ReadonlyTodo = Readonly<Todo>;
// Record<K, T> - 构造一个对象类型,属性键为 K,属性值为 T
interface PageInfo {
title: string;
}
type Pages = "home" | "about" | "contact";
const nav: Record<Pages, PageInfo> = {
home: { title: "Home" },
about: { title: "About" },
contact: { title: "Contact" }
};
// Pick<T, K> - 从 T 中选择一组属性 K
type TodoPreview = Pick<Todo, "title">;
// Omit<T, K> - 从 T 中排除一组属性 K
type TodoInfo = Omit<Todo, "description">;
// Exclude<T, U> - 从 T 中排除可分配给 U 的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U> - 从 T 中提取可分配给 U 的类型
type T1 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"
// NonNullable<T> - 从 T 中排除 null 和 undefined
type T2 = NonNullable<string | number | null | undefined>; // string | number
// Parameters<T> - 获取函数参数类型
type T3 = Parameters<(s: string, n: number) => void>; // [string, number]
// ReturnType<T> - 获取函数返回值类型
type T4 = ReturnType<() => string>; // string
4. 类型声明文件
4.1 什么是类型声明文件?
类型声明文件(.d.ts)用于为 JavaScript 代码提供类型信息,让 TypeScript 能够理解非 TypeScript 代码。
4.2 模块声明
typescript
// 声明 CSS 模块
declare module '*.css' {
const content: any;
export default content;
}
// 声明 Less 模块
declare module '*.less' {
const content: any;
export default content;
}
// 声明 SCSS 模块
declare module '*.scss' {
const content: any;
export default content;
}
// 声明图片模块
declare module '*.png' {
const src: string;
export default src;
}
// 声明 JSON 模块
declare module '*.json' {
const value: any;
export default value;
}
4.3 使用场景
typescript
// 在 Vue/React 项目中导入样式
import './style.css';
import styles from './theme.less';
// 在 Vite/Webpack 中使用
// tsconfig.json 中需要配置:
// {
// "compilerOptions": {
// "moduleResolution": "node",
// "allowImportingTsExtensions": true
// }
// }
4.4 全局声明
typescript
// 声明全局变量
declare const API_URL: string;
// 声明全局函数
declare function ajax(url: string): Promise<any>;
// 扩展全局命名空间
declare global {
interface Window {
myPlugin: any;
}
}
5. 最佳实践
5.1 类型选择优先级
typescript
// ✅ 优先使用具体类型
let name: string = "Alice";
let age: number = 30;
// ✅ 其次使用接口或类型别名
interface User {
id: number;
name: string;
}
// ⚠️ 谨慎使用 any
let value: any; // 避免
// ✅ 使用 unknown 代替 any
let unknownValue: unknown;
// ✅ 使用类型推断
let count = 0; // 不需要显式标注 : number
5.2 接口设计原则
typescript
// ✅ 保持接口简洁
interface User {
id: number;
name: string;
email: string;
}
// ✅ 使用可选属性
interface Config {
debug?: boolean;
timeout?: number;
}
// ✅ 使用只读属性
interface ImmutablePoint {
readonly x: number;
readonly y: number;
}
// ✅ 接口继承扩展
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
5.3 泛型使用指南
typescript
// ✅ 有意义的泛型命名
function identity<T>(arg: T): T {
return arg;
}
// ✅ 多个泛型参数
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
// ✅ 泛型约束
function logLength<T extends { length: number }>(arg: T): void {
console.log(arg.length);
}
logLength("hello"); // ✅
// logLength(42); // ❌
5.4 避免的陷阱
typescript
// ❌ 过度使用 any
function process(data: any): any {
return data;
}
// ✅ 使用泛型
function process<T>(data: T): T {
return data;
}
// ❌ 类型断言滥用
let value: any = "hello";
let length = (value as string).length;
// ✅ 先进行类型检查
if (typeof value === "string") {
let length = value.length;
}
// ❌ 忽略严格模式
// tsconfig.json 中应启用 strict: true
// ✅ 启用所有严格检查
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
5.5 项目配置建议
json
{
"compilerOptions": {
// 基础配置
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
// 严格模式
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
// 模块解析
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// 输出
"outDir": "./dist",
"declaration": true,
"sourceMap": true,
// 代码质量
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitThis": true,
"alwaysStrict": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
5.6 编码规范
typescript
// ✅ 好的实践
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
type EventType = 'click' | 'hover' | 'focus';
// ❌ 避免使用 any
let data: any; // 不好
let data: unknown; // 更好
// ✅ 使用类型推断
const arr = [1, 2, 3]; // 自动推断为 number[]
// ✅ 推荐:定义对象结构
interface User {
id: number;
name: string;
email: string;
}
// ✅ 推荐:定义函数类型
interface ApiFunction {
(url: string, data?: any): Promise<any>;
}
// ✅ 推荐:定义类结构
interface Service {
init(): void;
destroy(): void;
}
class UserService implements Service {
init() { console.log('初始化'); }
destroy() { console.log('销毁'); }
}
6. 学习路线与建议
6.1 学习路线
初级阶段
↓
基础类型(string, number, boolean)
接口和类型别名
枚举、元组、数组
↓
中级阶段
↓
泛型概念和应用
接口继承和实现
类型推断和类型守卫
联合类型和交叉类型
↓
高级阶段
↓
条件类型和映射类型
类型声明文件编写
高级工具类型(Pick, Omit, Partial 等)
实际项目应用
6.2 实战最佳实践
何时使用接口?
typescript
// ✅ 推荐:定义对象结构
interface User {
id: number;
name: string;
email: string;
}
// ✅ 推荐:定义函数类型
interface ApiFunction {
(url: string, data?: any): Promise<any>;
}
// ✅ 推荐:定义类结构
interface Service {
init(): void;
destroy(): void;
}
class UserService implements Service {
init() { console.log('初始化'); }
destroy() { console.log('销毁'); }
}
何时使用泛型?
typescript
// ✅ 推荐:通用工具函数
function wrapInArray<T>(value: T): T[] {
return [value];
}
// ✅ 推荐:API 响应类型
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
// 使用
const response: ApiResponse<User> = {
code: 200,
data: { id: 1, name: '张三', email: 'test@example.com' },
message: 'success'
};
6.3 常见问题解答
Q1: Interface 和 Type 选哪个?
A:
- 定义对象结构 → Interface
- 定义联合类型 → Type
- 需要被类实现 → Interface
- 需要类型别名 → Type
Q2: 什么时候用泛型?
A: 当你需要编写可复用 的代码,且这个代码需要处理多种类型时。
Q3: any 和 unknown 有什么区别?
A:
any: 放弃类型检查,不安全unknown: 需要类型收窄后才能使用,更安全
Q4: 如何选择合适的类型注解?
A:
- 优先让 TypeScript 自动推断
- 函数参数和返回值明确标注
- 对象和数组使用接口或类型别名
- 避免使用
any
📝 下篇总结
关键要点
✅ 类型断言用于告诉编译器具体类型
✅ 类型守卫在运行时检查类型
✅ 掌握条件类型、映射类型等高级特性
✅ 理解并使用内置工具类型
✅ 遵循最佳实践,编写类型安全的代码
完整学习检查清单
上篇内容回顾
- TypeScript 概述和优势
- 编译器安装和配置
- 基础类型(string, number, boolean, array)
- 联合类型、交叉类型
- 枚举和元组
中篇内容回顾
- 接口定义和使用
- 类实现接口
- Interface vs Type
- Interface vs 抽象类
- 泛型函数、接口、类
下篇内容回顾
- 类型转换和类型断言
- 类型守卫(typeof, instanceof, in)
- 条件类型和映射类型
- 内置工具类型
- 类型声明文件
- 最佳实践和项目配置
🎯 TypeScript 的核心价值
TypeScript 的核心价值在于:
- ✅ 类型安全:在编译时发现错误
- ✅ 代码提示:IDE 智能补全
- ✅ 重构友好:修改代码更自信
- ✅ 文档即代码:类型即文档
记住:TypeScript 的目标不是限制你,而是帮助你写出更好的代码!
📖 快速参考表
基础类型
| 类型 | 语法 | 示例 |
|---|---|---|
| 字符串 | string |
let name: string = 'tom' |
| 数字 | number |
let age: number = 18 |
| 布尔 | boolean |
let isOk: boolean = true |
| 数组 | type[] |
let arr: number[] = [1,2,3] |
| 元组 | [T, U] |
let tuple: [string, number] |
| 枚举 | enum |
enum Color { Red, Green } |
| 任意 | any |
let value: any = 4 |
| 空值 | void |
function(): void {} |
| 永不 | never |
function(): never {} |
| 对象 | object |
let obj: object = {} |
常用工具类型
| 工具类型 | 作用 | 示例 |
|---|---|---|
Partial<T> |
将属性设为可选 | Partial<User> |
Required<T> |
将属性设为必需 | Required<User> |
Readonly<T> |
将属性设为只读 | Readonly<User> |
Pick<T, K> |
选择特定属性 | Pick<User, 'name'> |
Omit<T, K> |
省略特定属性 | Omit<User, 'age'> |
Record<K, T> |
构造对象类型 | Record<string, number> |
ReturnType<T> |
获取返回类型 | ReturnType<fn> |
Parameters<T> |
获取参数类型 | Parameters<fn> |