TypeScript支持 #
一、TypeScript 配置 #
1.1 基础配置 #
json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
1.2 类型声明文件 #
typescript
// src/types/tauri.d.ts
declare module '@tauri-apps/api/core' {
export function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T>;
}
declare module '@tauri-apps/api/event' {
export interface Event<T> {
event: string;
id: number;
payload: T;
windowLabel: string;
}
export function listen<T>(
event: string,
handler: (event: Event<T>) => void
): Promise<() => void>;
export function emit(event: string, payload?: unknown): Promise<void>;
export function emitTo(
target: string,
event: string,
payload?: unknown
): Promise<void>;
}
二、命令类型定义 #
2.1 定义命令类型映射 #
typescript
// src/types/commands.ts
export interface CommandMap {
'get_user': {
params: { id: number };
result: User;
};
'create_user': {
params: { name: string; email: string };
result: User;
};
'delete_user': {
params: { id: number };
result: void;
};
'get_config': {
params: { key: string };
result: string | null;
};
'set_config': {
params: { key: string; value: string };
result: void;
};
}
export interface User {
id: number;
name: string;
email: string;
createdAt: string;
}
2.2 类型安全的 invoke 函数 #
typescript
// src/utils/invoke.ts
import { invoke as tauriInvoke } from '@tauri-apps/api/core';
import type { CommandMap } from '../types/commands';
type CommandKey = keyof CommandMap;
export async function invoke<K extends CommandKey>(
cmd: K,
args: CommandMap[K]['params']
): Promise<CommandMap[K]['result']> {
return tauriInvoke(cmd, args);
}
// 可选参数版本
export async function invokeOptional<K extends CommandKey>(
cmd: K,
args?: Partial<CommandMap[K]['params']>
): Promise<CommandMap[K]['result']> {
return tauriInvoke(cmd, args);
}
2.3 使用类型安全的 invoke #
typescript
import { invoke } from './utils/invoke';
// 类型安全的调用
const user = await invoke('get_user', { id: 1 });
// user 的类型自动推断为 User
// 错误:参数类型不匹配
// await invoke('get_user', { id: 'string' }); // 编译错误
三、事件类型定义 #
3.1 定义事件类型映射 #
typescript
// src/types/events.ts
export interface EventMap {
'user:login': { userId: string; token: string };
'user:logout': void;
'config:update': { key: string; value: unknown };
'notification:show': { title: string; message: string; type: 'info' | 'error' };
'file:changed': { path: string; content: string };
}
3.2 类型安全的事件函数 #
typescript
// src/utils/events.ts
import { listen as tauriListen, emit as tauriEmit, emitTo as tauriEmitTo } from '@tauri-apps/api/event';
import type { EventMap } from '../types/events';
type EventKey = keyof EventMap;
export async function listen<K extends EventKey>(
event: K,
handler: (payload: EventMap[K]) => void
): Promise<() => void> {
return tauriListen<EventMap[K]>(event, (e) => {
handler(e.payload);
});
}
export async function emit<K extends EventKey>(
event: K,
payload: EventMap[K]
): Promise<void> {
return tauriEmit(event, payload);
}
export async function emitTo<K extends EventKey>(
target: string,
event: K,
payload: EventMap[K]
): Promise<void> {
return tauriEmitTo(target, event, payload);
}
3.3 使用类型安全的事件 #
typescript
import { listen, emit } from './utils/events';
// 发送事件
await emit('user:login', { userId: '123', token: 'abc' });
// 监听事件
const unlisten = await listen('user:login', (payload) => {
// payload 类型自动推断为 { userId: string; token: string }
console.log(payload.userId, payload.token);
});
四、Rust 与 TypeScript 类型同步 #
4.1 Rust 结构体 #
rust
// src-tauri/src/models/user.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u32,
pub name: String,
pub email: String,
pub created_at: String,
}
4.2 TypeScript 类型生成 #
使用 ts-rs crate 自动生成 TypeScript 类型:
toml
# Cargo.toml
[dependencies]
ts-rs = "7"
rust
// src-tauri/src/models/user.rs
use serde::{Deserialize, Serialize};
use ts_rs::TS;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct User {
pub id: u32,
pub name: String,
pub email: String,
pub created_at: String,
}
生成的 TypeScript 类型:
typescript
// generated/User.ts
export interface User {
id: number;
name: string;
email: string;
created_at: string;
}
4.3 构建脚本 #
rust
// src-tauri/build.rs
fn main() {
tauri_build::build();
// 生成 TypeScript 类型
println!("cargo:rerun-if-changed=src/models");
}
五、API 客户端封装 #
5.1 创建 API 客户端 #
typescript
// src/api/client.ts
import { invoke } from '../utils/invoke';
export const api = {
user: {
async get(id: number) {
return invoke('get_user', { id });
},
async create(name: string, email: string) {
return invoke('create_user', { name, email });
},
async delete(id: number) {
return invoke('delete_user', { id });
},
},
config: {
async get(key: string) {
return invoke('get_config', { key });
},
async set(key: string, value: string) {
return invoke('set_config', { key, value });
},
},
};
5.2 使用 API 客户端 #
typescript
import { api } from './api/client';
// 获取用户
const user = await api.user.get(1);
// 创建用户
const newUser = await api.user.create('Alice', 'alice@example.com');
// 删除用户
await api.user.delete(1);
六、React Hooks 类型 #
6.1 类型安全的 Hook #
typescript
// src/hooks/useCommand.ts
import { useState, useCallback } from 'react';
import { invoke as tauriInvoke } from '@tauri-apps/api/core';
import type { CommandMap } from '../types/commands';
type CommandKey = keyof CommandMap;
interface UseCommandResult<K extends CommandKey> {
data: CommandMap[K]['result'] | null;
loading: boolean;
error: string | null;
execute: (params: CommandMap[K]['params']) => Promise<CommandMap[K]['result']>;
}
export function useCommand<K extends CommandKey>(cmd: K): UseCommandResult<K> {
const [data, setData] = useState<CommandMap[K]['result'] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const execute = useCallback(
async (params: CommandMap[K]['params']) => {
setLoading(true);
setError(null);
try {
const result = await tauriInvoke<CommandMap[K]['result']>(cmd, params);
setData(result);
return result;
} catch (err) {
setError(String(err));
throw err;
} finally {
setLoading(false);
}
},
[cmd]
);
return { data, loading, error, execute };
}
6.2 使用 Hook #
tsx
import { useCommand } from '../hooks/useCommand';
function UserProfile() {
const { data: user, loading, error, execute } = useCommand('get_user');
useEffect(() => {
execute({ id: 1 });
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
七、类型守卫 #
7.1 类型守卫函数 #
typescript
// src/utils/guards.ts
import type { User } from '../types/commands';
export function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'email' in value &&
typeof (value as User).id === 'number' &&
typeof (value as User).name === 'string' &&
typeof (value as User).email === 'string'
);
}
export function isUserArray(value: unknown): value is User[] {
return Array.isArray(value) && value.every(isUser);
}
7.2 使用类型守卫 #
typescript
import { isUser } from '../utils/guards';
const result = await invoke('get_user', { id: 1 });
if (isUser(result)) {
console.log(result.name); // 类型安全
} else {
console.error('Invalid user data');
}
八、错误类型 #
8.1 定义错误类型 #
typescript
// src/types/errors.ts
export interface AppError {
code: string;
message: string;
details?: Record<string, unknown>;
}
export class CommandError extends Error {
code: string;
details?: Record<string, unknown>;
constructor(error: AppError) {
super(error.message);
this.code = error.code;
this.details = error.details;
this.name = 'CommandError';
}
}
export function isAppError(value: unknown): value is AppError {
return (
typeof value === 'object' &&
value !== null &&
'code' in value &&
'message' in value
);
}
8.2 错误处理 #
typescript
import { CommandError, isAppError } from '../types/errors';
async function safeInvoke<T>(cmd: string, args: unknown): Promise<T> {
try {
return await invoke<T>(cmd, args);
} catch (error) {
if (isAppError(error)) {
throw new CommandError(error);
}
throw new Error(String(error));
}
}
九、最佳实践 #
9.1 类型组织 #
text
src/types/
├── commands.ts # 命令类型
├── events.ts # 事件类型
├── models.ts # 数据模型
├── errors.ts # 错误类型
└── index.ts # 统一导出
9.2 类型复用 #
typescript
// src/types/models.ts
export interface BaseEntity {
id: number;
createdAt: string;
updatedAt: string;
}
export interface User extends BaseEntity {
name: string;
email: string;
}
export interface Post extends BaseEntity {
title: string;
content: string;
authorId: number;
}
9.3 泛型工具 #
typescript
// src/utils/types.ts
export type AsyncResult<T> = {
data: T | null;
loading: boolean;
error: string | null;
};
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
export type PickByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P];
};
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 类型定义 | 定义命令和事件类型映射 |
| 类型安全 | 封装类型安全的 invoke |
| 类型同步 | Rust 和 TypeScript 类型同步 |
| 类型守卫 | 运行时类型检查 |
| 错误处理 | 定义错误类型 |
10.2 下一步 #
现在你已经掌握了 TypeScript 支持,接下来让我们学习 本地存储,了解如何在 Tauri 应用中存储数据!
最后更新:2026-03-28