能否为Firebase的httpsCallable函数指定输入与返回类型?
在Firebase Callable函数中实现客户端与服务端的类型共享
问题背景
我用TypeScript编写Firebase云函数,服务端已经定义了GivePermissionsParams接口描述调用参数的结构。现在需要在客户端也为这个Callable函数指定参数和返回类型,让团队开发者不用查看服务端代码,就能清楚知道函数的调用要求和返回格式。
服务端原代码(functions/src/index.ts):
// 希望在客户端指定的所需数据结构 interface GivePermissionsParams { uid: string; email: string; newClaim: Partial<Permissions>; } /** * 用于为用户授予新权限的Callable函数,权限以自定义声明和Firestore属性形式存在 */ exports.givePermission = functions.https.onCall( async (data: GivePermissionsParams, context) => { if (!context.auth?.token.admin) { throw new HttpsError( 'permission-denied', `Non admin user ${context.auth?.uid} attempted to update permissions` ); } return grantPermission(data.uid, data.newClaim).then(() => { log(`Successfully updated permissions for ${data.email}`); return { result: `Successfully updated permissions for ${data.email}`, }; }); } );
客户端原代码:
// firebase.ts // 我希望在此处指定函数参数和返回类型,例如:httpsCallable<myParamsType, myReturnType> export const givePermission = httpsCallable(functions, 'givePermission');
// reactComponent.tsx const changePermission = async (permission: string, value: boolean) => { // 该参数应与函数index.ts文件中定义的GivePermissionsParams类型匹配 const functionParams = { uid: user.uid, email: user.email, newClaim: {[permission]: value} } const functionRes = await givePermission(functionParams); };
解决方案:共享类型定义
核心思路是把服务端和客户端共用的类型抽离到一个共享文件中,两端都导入这个文件的类型,确保类型一致且无需重复定义。
1. 创建共享类型文件
在项目中创建一个跨端可访问的类型文件(比如shared/firebase-types.ts),把需要共享的接口和类型定义放进去:
// shared/firebase-types.ts // 定义Permissions基础类型(服务端原代码中用到的Partial<Permissions>) export interface Permissions { // 补充你的具体权限字段,示例: editPosts: boolean; manageUsers: boolean; viewAnalytics: boolean; } // 定义Callable函数的参数类型 export interface GivePermissionsParams { uid: string; email: string; newClaim: Partial<Permissions>; } // 定义Callable函数的返回类型 export interface GivePermissionResult { result: string; }
2. 服务端更新:使用共享类型
修改服务端的functions/src/index.ts,导入共享类型替换原有的接口定义:
// functions/src/index.ts import { GivePermissionsParams, GivePermissionResult } from '../../shared/firebase-types'; /** * 用于为用户授予新权限的Callable函数,权限以自定义声明和Firestore属性形式存在 */ exports.givePermission = functions.https.onCall( async (data: GivePermissionsParams, context): Promise<GivePermissionResult> => { if (!context.auth?.token.admin) { throw new HttpsError( 'permission-denied', `Non admin user ${context.auth?.uid} attempted to update permissions` ); } return grantPermission(data.uid, data.newClaim).then(() => { log(`Successfully updated permissions for ${data.email}`); return { result: `Successfully updated permissions for ${data.email}`, }; }); } );
3. 客户端更新:类型化Callable函数
在客户端的firebase.ts中,导入共享类型并通过httpsCallable的泛型指定参数和返回类型:
// firebase.ts import { httpsCallable } from 'firebase/functions'; import { functions } from './firebase-config'; // 你的Firebase配置导入 import { GivePermissionsParams, GivePermissionResult } from '../shared/firebase-types'; // 为Callable函数指定参数和返回类型 export const givePermission = httpsCallable<GivePermissionsParams, GivePermissionResult>(functions, 'givePermission');
4. 客户端使用:获得类型提示
现在在React组件中调用函数时,TypeScript会自动提供参数校验和返回值类型提示:
// reactComponent.tsx import { givePermission } from './firebase'; import { Permissions } from '../shared/firebase-types'; const changePermission = async (permission: keyof Permissions, value: boolean) => { // 参数会自动校验是否符合GivePermissionsParams类型 const functionParams = { uid: user.uid, email: user.email, newClaim: {[permission]: value} }; // functionRes的类型会被自动推导为GivePermissionResult const functionRes = await givePermission(functionParams); console.log(functionRes.data.result); // 可直接访问result字段,有类型提示 };
优势
- 类型单一来源,避免两端类型不一致的问题
- 开发者在客户端编写代码时,无需查看服务端代码就能知道参数要求和返回格式
- TypeScript会自动校验参数合法性,提前发现错误
内容的提问来源于stack exchange,提问作者Rafael Zasas
相关产品推荐
相关产品推荐

