TypeScript从函数接口提取返回类型时出现类型推导错误
问题原因
两个核心类型定义错误导致类型校验不符合预期:
EdmSchema条件判断作用域错误
原写法对整个传入的根类型T做对象校验,而非对每个属性的类型T[K]做校验:
当传入的// 错误实现:判断根类型T是否为对象,而非判断每个属性值T[K] type EdmSchema<T> = {[K in keyof T]: T extends object ? EdmSchemaValue<T[K]> : never};T是数组类型(比如IHostEntry[])时,TS会将数组识别为对象,映射出的类型是包含数组原型方法(push/pop/length等)的普通对象结构,而非预期的「元素Schema组成的数组」结构,直接导致后续returnType的数组匹配逻辑失效。- 数组场景类型匹配缺失
当函数返回值是数组类型U[]时,原定义EdmMap<RT> | EdmSchema<RT>没有明确声明「数组元素可以是EdmSchema<U>类型」,TS会默认将数组元素匹配为基础类型对应的EdmSchemaValue结构,要求元素必须自带顶层type字段,因此会把hostDataSchema(本身是EdmSchema<IHostEntry>结构,无顶层type字段)判定为类型不匹配,抛出缺少type属性的错误。
修正方案
调整类型定义逻辑,明确区分数组、对象、基础类型的映射规则,补全缺失的工具类型定义:
- 重构
EdmMap映射逻辑,自动识别数组、基础类型、对象类型,分别映射到对应Edm类型结构 - 修正
EdmSchema定义,对每个属性值做映射,支持嵌套对象递归校验 - 简化
EdmFunctionSchema的returnType定义,直接复用EdmMap的自动推导能力,覆盖所有返回值场景
修正后的完整可运行代码:
// Edm原始类型定义,和现有用法对齐 namespace Edm { export const String = 'Edm.String' as const; // 可按相同方式扩展Number、Boolean等其他原始类型 } // 基础类型到Edm原始类型的映射 type EdmPrimitiveMap<T> = T extends string ? typeof Edm.String : never; // 核心Edm类型映射:自动区分数组、基础类型、对象 type EdmMap<T> = // 数组类型:映射为元素对应Edm类型的数组 T extends Array<infer U> ? Array<EdmMap<U>> : // 基础类型:映射到对应Edm原始类型 T extends string | number | boolean ? EdmPrimitiveMap<T> : // 对象类型:映射为EdmSchema结构 T extends object ? EdmSchema<T> : never; type EdmSchemaValue<T> = { type: EdmMap<T>; nullable?: boolean; }; // 修正后的EdmSchema:所有属性统一映射为EdmSchemaValue type EdmSchema<T extends object> = { [K in keyof T]: EdmSchemaValue<T[K]> }; // 补全缺失的工具类型 type UnPromisify<T> = T extends Promise<infer U> ? U : T; type FunctionWithAgs = (...args: any[]) => any; type EdmParameterArray<T extends FunctionWithAgs> = Array<{ type: EdmMap<Parameters<T>[number]>; name: string; nullable?: boolean; }>; type EdmFunctionObject<T> = { [K in keyof T]: T[K] extends FunctionWithAgs ? EdmFunctionSchema<T[K]> : never }; // 修正后的函数Schema定义 type EdmFunctionSchema<T extends FunctionWithAgs, RT = UnPromisify<ReturnType<T>>> = { params: EdmParameterArray<T>; returnType: EdmMap<RT>; }; // 测试接口 interface IHostEntry { address: string; hostname: string; aliases: string[]; } // 无类型报错 const hostDataSchema: EdmSchema<IHostEntry> = { address: {type: Edm.String, nullable: false}, hostname: {type: Edm.String, nullable: false}, aliases: {type: [Edm.String], nullable: false}, }; interface IFunc { list(all: string): Promise<IHostEntry[]>; } // 无类型报错,returnType被正确推导为EdmSchema<IHostEntry>[] const functionSchema: EdmFunctionObject<IFunc> = { list: {params: [{type: Edm.String, name: 'all', nullable: false}], returnType: [hostDataSchema]}, };
修正效果
hostDataSchema不再抛出缺少type属性的类型错误- 数组场景下
returnType会被正确推导:当返回值为IHostEntry[]时,returnType类型为EdmSchema<IHostEntry>[],可正确识别[hostDataSchema]为合法值 - 天然支持嵌套对象、多层数组的类型校验,后续扩展复杂实体类型不会出现匹配逻辑错误
内容的提问来源于stack exchange,提问作者Manda
相关产品推荐
相关产品推荐

