TypeScript类型守卫 #
一、类型守卫概述 #
1.1 什么是类型守卫 #
类型守卫是TypeScript中用于在运行时检查类型,从而在编译时收窄类型的表达式。
typescript
function process(value: string | number): void {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
1.2 类型收窄 #
类型守卫会将类型从宽泛的类型收窄到更具体的类型:
typescript
let value: string | number | boolean;
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else if (typeof value === 'number') {
console.log(value.toFixed(2));
} else {
console.log(value);
}
二、typeof类型守卫 #
2.1 基本用法 #
typescript
function process(value: string | number): void {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
2.2 typeof支持的类型 #
typeof可以检测以下类型:
'string''number''boolean''symbol''bigint''undefined''object''function'
typescript
function inspect(value: unknown): void {
if (typeof value === 'string') {
console.log('String:', value);
} else if (typeof value === 'number') {
console.log('Number:', value);
} else if (typeof value === 'boolean') {
console.log('Boolean:', value);
} else if (typeof value === 'object') {
console.log('Object:', value);
} else if (typeof value === 'function') {
console.log('Function:', value);
} else {
console.log('Unknown type');
}
}
2.3 typeof的局限性 #
typeof null返回'object':
typescript
function process(value: object | null): void {
if (typeof value === 'object') {
console.log(value.toString());
}
}
function processSafe(value: object | null): void {
if (value !== null && typeof value === 'object') {
console.log(value.toString());
}
}
三、instanceof类型守卫 #
3.1 基本用法 #
typescript
class Dog {
bark(): void {
console.log('Woof!');
}
}
class Cat {
meow(): void {
console.log('Meow!');
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
3.2 内置类型 #
typescript
function process(value: Date | string): void {
if (value instanceof Date) {
console.log(value.toISOString());
} else {
console.log(value.toUpperCase());
}
}
function processError(error: Error | string): void {
if (error instanceof Error) {
console.log(error.message);
} else {
console.log(error);
}
}
3.3 数组检查 #
typescript
function process(value: string | string[]): void {
if (Array.isArray(value)) {
value.forEach(item => console.log(item));
} else {
console.log(value);
}
}
四、in操作符类型守卫 #
4.1 基本用法 #
typescript
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function move(animal: Bird | Fish): void {
if ('fly' in animal) {
animal.fly();
} else {
animal.swim();
}
}
4.2 检查可选属性 #
typescript
interface User {
name: string;
email?: string;
}
function process(user: User): void {
if ('email' in user) {
console.log(user.email);
}
}
4.3 检查方法存在 #
typescript
interface Serializable {
serialize(): string;
}
function process(value: object): void {
if ('serialize' in value && typeof value.serialize === 'function') {
console.log(value.serialize());
}
}
五、字面量类型守卫 #
5.1 字符串字面量 #
typescript
type Direction = 'up' | 'down' | 'left' | 'right';
function move(direction: Direction): void {
switch (direction) {
case 'up':
console.log('Moving up');
break;
case 'down':
console.log('Moving down');
break;
case 'left':
console.log('Moving left');
break;
case 'right':
console.log('Moving right');
break;
}
}
5.2 可辨识联合 #
typescript
interface Circle {
kind: 'circle';
radius: number;
}
interface Square {
kind: 'square';
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
}
}
5.3 穷尽性检查 #
typescript
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
default:
const _exhaustiveCheck: never = shape;
throw new Error(`Unknown shape: ${_exhaustiveCheck}`);
}
}
六、自定义类型守卫 #
6.1 类型谓词 #
使用is关键字定义类型谓词:
typescript
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function process(value: unknown): void {
if (isString(value)) {
console.log(value.toUpperCase());
}
}
6.2 对象类型守卫 #
typescript
interface User {
name: string;
age: number;
}
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'name' in value &&
'age' in value &&
typeof (value as User).name === 'string' &&
typeof (value as User).age === 'number'
);
}
function process(value: unknown): void {
if (isUser(value)) {
console.log(value.name, value.age);
}
}
6.3 数组类型守卫 #
typescript
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every(item => typeof item === 'string');
}
function process(value: unknown): void {
if (isStringArray(value)) {
value.forEach(item => console.log(item.toUpperCase()));
}
}
6.4 断言函数 #
typescript
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Value is not a string');
}
}
function process(value: unknown): void {
assertIsString(value);
console.log(value.toUpperCase());
}
6.5 断言函数与错误 #
typescript
function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T {
if (value === null || value === undefined) {
throw new Error(message ?? 'Value is null or undefined');
}
}
function process(value: string | null): void {
assertDefined(value);
console.log(value.toUpperCase());
}
七、真值检查 #
7.1 真值收窄 #
typescript
function process(value: string | null | undefined): void {
if (value) {
console.log(value.toUpperCase());
}
}
7.2 排除null和undefined #
typescript
function process(value: string | null | undefined): string {
if (value == null) {
return 'default';
}
return value.toUpperCase();
}
7.3 Boolean转换 #
typescript
function process(values: (string | null | undefined)[]): string[] {
return values.filter(Boolean);
}
const filtered = process(['a', null, 'b', undefined, 'c']);
console.log(filtered);
八、相等性收窄 #
8.1 相等检查 #
typescript
function process(value: string | number, expected: string): void {
if (value === expected) {
console.log(value.toUpperCase());
}
}
8.2 switch语句 #
typescript
type Status = 'pending' | 'approved' | 'rejected';
function handleStatus(status: Status): string {
switch (status) {
case 'pending':
return 'Waiting for approval';
case 'approved':
return 'Approved';
case 'rejected':
return 'Rejected';
}
}
九、实用示例 #
9.1 API响应处理 #
typescript
interface SuccessResponse<T> {
success: true;
data: T;
}
interface ErrorResponse {
success: false;
error: string;
}
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
function isSuccess<T>(response: ApiResponse<T>): response is SuccessResponse<T> {
return response.success === true;
}
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
const data: ApiResponse<T> = await response.json();
if (isSuccess(data)) {
return data.data;
}
throw new Error(data.error);
}
9.2 表单验证 #
typescript
interface ValidForm {
isValid: true;
values: Record<string, string>;
}
interface InvalidForm {
isValid: false;
errors: Record<string, string>;
}
type FormResult = ValidForm | InvalidForm;
function validateForm(formData: FormData): FormResult {
const values: Record<string, string> = {};
const errors: Record<string, string> = {};
formData.forEach((value, key) => {
if (typeof value === 'string') {
if (value.trim() === '') {
errors[key] = 'This field is required';
} else {
values[key] = value;
}
}
});
if (Object.keys(errors).length === 0) {
return { isValid: true, values };
}
return { isValid: false, errors };
}
function processForm(result: FormResult): void {
if (result.isValid) {
console.log('Submitting:', result.values);
} else {
console.log('Errors:', result.errors);
}
}
十、总结 #
本章介绍了TypeScript类型守卫:
类型守卫类型 #
- typeof:检查原始类型
- instanceof:检查类实例
- in:检查属性存在
- 字面量:检查具体值
- 自定义:使用类型谓词
最佳实践 #
- 使用可辨识联合提高类型安全性
- 创建可复用的类型守卫函数
- 使用断言函数进行强制检查
- 在switch语句中进行穷尽性检查
最后更新:2026-03-26