在 TypeScript 中,校验接口返回的数据是否符合定义的类型,通常需要结合运行时检查和 TypeScript 的静态类型系统。TypeScript 的类型检查仅在编译时生效,而接口返回的数据是运行时获取的,因此需要额外的工具或方法来确保数据符合预期类型。
直接使用类型断言告诉 TypeScript 你相信接口返回的数据符合定义的类型。这种方法不进行运行时校验,仅依赖开发者的假设。
typescript
interface User {
id: number;
name: string;
}
async function fetchUser(): Promise<User> {
const response = await fetch('/api/user');
const data = await response.json() as User; // 类型断言
return data;
}
为了在运行时校验数据类型,手动编写校验逻辑,在运行时检查返回数据的结构和类型是否符合 TypeScript 定义。这种方法需要每次手写校验函数,不实用。
typescript
interface User {
id: number;
name: string;
}
function isUser(data: unknown): data is User {
if (typeof data !== 'object' || data === null) return false;
const user = data as User;
return (
typeof user.id === 'number' &&
typeof user.name === 'string'
);
}
async function fetchUser(): Promise<User> {
const response = await fetch('/api/user');
const data = await response.json();
if (!isUser(data)) {
throw new Error('返回的数据不符合 User 类型');
}
return data;
}
fetchUser()
.then(user => console.log(user.id, user.name))
.catch(err => console.error(err));
使用第三方库(如 zod
或 io-ts
)
借助成熟的运行时类型校验库,可以更优雅地定义和校验接口返回的数据。这些库同时支持 TypeScript 类型推导和运行时验证。
使用 zod
zod
是一个流行的 TypeScript 运行时类型校验库,支持类型定义和校验。
-
安装:
bashnpm install zod
-
定义 Schema 并校验:
typescriptimport { z } from 'zod'; // 定义 Schema,同时生成 TypeScript 类型会自动推导 const UserSchema = z.object({ id: z.number(), name: z.string(), }); // TypeScript 类型 type User = z.infer<typeof UserSchema>; async function fetchUser(): Promise<User> { const response = await fetch('/api/user'); const data = await response.json(); // 校验数据 return UserSchema.parse(data); // 如果不符合 Schema,会抛出错误 } fetchUser() .then(user => console.log(user.id, user.name)) .catch(err => console.error('校验失败:', err));
- 优点 :
- 类型安全:
z.infer
自动生成 TypeScript 类型。 - 强大的校验功能:支持嵌套对象、数组、可选字段等。
- 错误信息详细,易于调试。
- 类型安全:
使用 io-ts
io-ts
是另一个专注于 TypeScript 的运行时类型校验库。
-
安装:
bashnpm install io-ts fp-ts
-
定义和校验:
typescriptimport * as t from 'io-ts'; import { either } from 'fp-ts/Either'; // 定义类型 const User = t.type({ id: t.number, name: t.string, }); // TypeScript 类型 type User = t.TypeOf<typeof User>; async function fetchUser(): Promise<User> { const response = await fetch('/api/user'); const data = await response.json(); const result = User.decode(data); if (either.isLeft(result)) { throw new Error('校验失败: ' + JSON.stringify(result.left)); } return result.right; }
- 优点:函数式编程风格,类型推导强大。
- 缺点 :学习曲线稍陡,依赖
fp-ts
。 - 适用场景 :喜欢函数式编程或已有
fp-ts
的项目。
方法 4:结合 JSON Schema
如果接口有标准的 JSON Schema 定义,可以使用工具(如 ajv
)进行校验。
-
安装:
bashnpm install ajv
-
定义和校验:
typescriptimport Ajv from 'ajv'; const ajv = new Ajv(); const userSchema = { type: 'object', properties: { id: { type: 'number' }, name: { type: 'string' }, }, required: ['id', 'name'], additionalProperties: false, }; interface User { id: number; name: string; } const validate = ajv.compile(userSchema); async function fetchUser(): Promise<User> { const response = await fetch('/api/user'); const data = await response.json(); if (!validate(data)) { throw new Error('校验失败: ' + JSON.stringify(validate.errors)); } return data as User; }
- 优点:与 JSON Schema 标准兼容,适合跨语言项目。
- 缺点:需要手动维护 TypeScript 类型和 Schema 的一致性。
- 适用场景:已有 JSON Schema 定义的项目。
最佳实践
-
选择合适的工具:
- 小型项目:手动校验或类型断言。
- 中大型项目:推荐
zod
,简单易用且功能强大。 - 复杂校验:
io-ts
或ajv
。
-
错误处理:
- 捕获校验错误并提供用户友好的提示。
- 记录校验失败的日志,便于调试。
-
与后端协作:
- 与后端约定数据格式,减少校验负担。
- 如果可能,使用 OpenAPI/Swagger 生成类型和校验。
-
性能考虑:
- 对于频繁调用的接口,避免过于复杂的校验逻辑。
示例:综合使用 zod
typescript
import { z } from 'zod';
const UserSchema = z.object({
id: z.number().positive(),
name: z.string().min(1),
email: z.string().email().optional(),
});
type User = z.infer<typeof UserSchema>;
async function fetchUser(id: number): Promise<User> {
try {
const response = await fetch(`/api/user/${id}`);
if (!response.ok) throw new Error('网络错误');
const data = await response.json();
return UserSchema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('数据校验失败:', error.errors);
throw new Error('无效的用户数据');
}
throw error;
}
}
fetchUser(1)
.then(user => console.log('用户:', user))
.catch(err => console.error('错误:', err));
总结
- 第三方库 (如
zod
):推荐,兼顾类型安全和运行时校验。 - JSON Schema:适合标准化项目。