TypeScript中传递unknown作为泛型的类型兼容问题排查
问题:aws-lambda-router中使用ProxyIntegrationEvent泛型时报TypeScript类型不兼容错误
我在使用aws-lambda-router这个npm包,它的简化类型定义如下:
export interface APIGatewayProxyStructuredResultV2 { statusCode?: number; headers?: { [header: string]: boolean | number | string; }; body?: string; isBase64Encoded?: boolean; cookies?: string[]; } export interface APIGatewayProxyEvent { version: string; routeKey: string; rawPath: string; body?: string; } type ProxyIntegrationBody<T = unknown> = { body?: T; }; type APIGatewayEventRequestContext = { key: string; } export type ProxyIntegrationEvent<T = unknown> = Omit<APIGatewayProxyEvent, 'body'> & ProxyIntegrationBody<T> export type ProxyIntegrationResult = Omit<APIGatewayProxyStructuredResultV2, 'statusCode'> & { statusCode?: APIGatewayProxyStructuredResultV2['statusCode']; }; export interface ProxyIntegrationRoute { path: string method: 'GET' | 'POST' | 'OPTIONS' | 'PUT'; action: ( request: ProxyIntegrationEvent<unknown>, context: APIGatewayEventRequestContext ) => ProxyIntegrationResult | Promise<ProxyIntegrationResult> | string | Promise<string> } export interface ProxyIntegrationConfig { routes: ProxyIntegrationRoute[]; }
我尝试为路由的action函数中request参数指定自定义泛型类型,但第二个路由出现了如下TypeScript错误:
Type '(request: ProxyIntegrationEvent<{ name: string; numbers: string[]; }>, context: APIGatewayEventRequestContext) => Promise<{ statusCode: number; body: string; }>' is not assignable to type '(request: ProxyIntegrationEvent<unknown>, context: APIGatewayEventRequestContext) => string | ProxyIntegrationResult | Promise<...> | Promise<...>'. Types of parameters 'request' and 'request' are incompatible. Type 'ProxyIntegrationEvent<unknown>' is not assignable to type 'ProxyIntegrationEvent<{ name: string; numbers: string[]; }>'. Type 'ProxyIntegrationEvent<unknown>' is not assignable to type 'ProxyIntegrationBody<{ name: string; numbers: string[]; }>'. Types of property 'body' are incompatible. Type 'unknown' is not assignable to type '{ name: string; numbers: string[]; } | undefined'.ts(2322) Untitled-1(29, 5): The expected type comes from property 'action' which is declared here on type 'ProxyIntegrationRoute'
如果移除泛型只使用ProxyIntegrationEvent,虽然能解决编译错误,但会失去request参数的类型提示,完全浪费了泛型的作用。
原因分析
这不是你的用法错误,而是包的类型定义存在设计缺陷:当前ProxyIntegrationRoute接口的action方法被固定为接收ProxyIntegrationEvent<unknown>类型的参数。而TypeScript中函数参数是逆变的——也就是说,一个期望接收更具体类型(比如你的自定义body类型)的函数,无法被赋值给期望接收更宽泛类型(unknown)的位置,因为unknown无法安全转换为你的自定义类型。
解决方案
临时 workaround(无需修改包源码)
在action内部用类型断言转换request的类型,既能保留类型提示,又能通过编译:
const handler: ProxyIntegrationConfig = { routes: [ // ...其他路由 { path: '/playedGames', method: 'POST', action: async (request, context) => { // 转换为自定义类型 const typedReq = request as ProxyIntegrationEvent<{ name: string, numbers: string[] }>; // 这里可以获得typedReq.body的类型提示 console.log(typedReq.body?.name); return { statusCode: 200, body: '' }; } }, ] };
永久修复(fork包修改类型定义)
将ProxyIntegrationRoute改为泛型接口,让每个路由可以独立指定request body的类型:
// 修改包中的ProxyIntegrationRoute定义 export interface ProxyIntegrationRoute<T = unknown> { path: string; method: 'GET' | 'POST' | 'OPTIONS' | 'PUT'; action: ( request: ProxyIntegrationEvent<T>, context: APIGatewayEventRequestContext ) => ProxyIntegrationResult | Promise<ProxyIntegrationResult> | string | Promise<string>; } // 对应的ProxyIntegrationConfig保持不变,因为TypeScript允许数组容纳不同泛型的同接口实例 export interface ProxyIntegrationConfig { routes: ProxyIntegrationRoute[]; }
修改后,你就能直接在定义路由时指定泛型,获得完整的类型提示且不会报错:
const handler: ProxyIntegrationConfig = { routes: [ { path: '/playedGames', method: 'POST', action: async (request: ProxyIntegrationEvent, context) => { return { statusCode: 200, body: '' }; } }, { path: '/playedGames', method: 'POST', action: async (request: ProxyIntegrationEvent<{ name: string, numbers: string[] }>, context) => { return { statusCode: 200, body: '' }; } }, ] };
内容的提问来源于stack exchange,提问作者Jorge Zapata
相关产品推荐
相关产品推荐

