TypeScript泛型函数内部返回类型为never的问题求助
问题分析与解决
问题描述
这段TypeScript代码中,getDetails函数内部的两个return语句都会被TypeScript提示返回类型为never,但调用该函数时,返回类型却能被正确推导。代码如下:
enum Partition { A = 'A', B = 'B', } type PartitionDetailsMap = { [Partition.A]: null; [Partition.B]: { [key: string]: { id: string; name: string; }; }; }; function isA(t): t is Partition.A { return true; } function isB(t): t is Partition.B { return true; } function getDetails<T extends Partition>(partition: T): PartitionDetailsMap[T] { if (isA(partition)) { return null; // 此处提示返回类型应为never } if (isB(partition)) { return { '1': { id: '1', name: 'ONE' }, '2': { id: '2', name: 'TWO' }, }; // 此处同样提示返回类型应为never } } const toto: null = getDetails(Partition.A); // 类型推导正确 const tata: { [key: string]: { id: string; name: string } } = getDetails(Partition.B); // 类型推导正确
原因
TypeScript在泛型函数内部,无法将泛型参数T与类型守卫缩小后的具体类型(Partition.A/Partition.B)建立精确关联。虽然类型守卫能确定当前分支中partition的类型,但TS没办法自动推断出当前分支的返回值正好匹配PartitionDetailsMap[T]的类型,因此会判定返回值不符合函数声明的返回类型,最终提示never。
另外,当前的类型守卫函数isA和isB未指定参数类型,也会削弱TS的类型推导能力。
解决办法
方法1:完善类型守卫 + 类型断言
先给类型守卫明确参数类型,再通过类型断言告诉TS当前返回值匹配泛型对应的类型:
enum Partition { A = 'A', B = 'B', } type PartitionDetailsMap = { [Partition.A]: null; [Partition.B]: { [key: string]: { id: string; name: string; }; }; }; // 明确参数类型为Partition,同时修正判断逻辑(原固定return true不合理) function isA(t: Partition): t is Partition.A { return t === Partition.A; } function isB(t: Partition): t is Partition.B { return t === Partition.B; } function getDetails<T extends Partition>(partition: T): PartitionDetailsMap[T] { if (isA(partition)) { return null as PartitionDetailsMap[T]; } if (isB(partition)) { return { '1': { id: '1', name: 'ONE' }, '2': { id: '2', name: 'TWO' }, } as PartitionDetailsMap[T]; } // 补充兜底逻辑,避免函数存在无返回的风险 throw new Error('Invalid partition type'); } const toto: null = getDetails(Partition.A); const tata: { [key: string]: { id: string; name: string } } = getDetails(Partition.B);
方法2:使用函数重载替代泛型
函数重载能明确指定每个输入对应的返回类型,TS内部可直接识别分支返回值的匹配性:
enum Partition { A = 'A', B = 'B', } type PartitionDetailsMap = { [Partition.A]: null; [Partition.B]: { [key: string]: { id: string; name: string; }; }; }; function isA(t: Partition): t is Partition.A { return t === Partition.A; } function isB(t: Partition): t is Partition.B { return t === Partition.B; } // 函数重载声明:明确每个输入对应的返回类型 function getDetails(partition: Partition.A): null; function getDetails(partition: Partition.B): PartitionDetailsMap[Partition.B]; // 函数实现 function getDetails(partition: Partition): PartitionDetailsMap[Partition] { if (isA(partition)) { return null; } if (isB(partition)) { return { '1': { id: '1', name: 'ONE' }, '2': { id: '2', name: 'TWO' }, }; } throw new Error('Invalid partition type'); } const toto: null = getDetails(Partition.A); const tata: { [key: string]: { id: string; name: string } } = getDetails(Partition.B);
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

