TypeScript泛型F为何可被约束的不同子类型实例化?类型报错解析
(...a: Parameters<F>) => number的不同子类型实例化? 代码示例
function wrap<F extends (...a: Parameters<F>) => number>(f: F): F { return (...a: Parameters<F>) => f(...a) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 此处报错 }
报错信息
英文原文:
Type '(...a: Parameters<F>) => number' is not assignable to type 'F'. '(...a: Parameters<F>) => number' is assignable to the constraint of type 'F', but 'F' could be instantiated with a different subtype of constraint '(...a: Parameters<F>) => number'.
中文翻译:
类型“(...a: Parameters) => number”无法赋值给类型“F”。“(...a: Parameters ) => number”可赋值给F的约束类型,但F可能被约束“(...a: Parameters ) => number”的不同子类型实例化。
原因解释
约束(...a: Parameters<F>) => number只是划定了F的最低要求:它必须是一个返回number的函数,但F可以是这个约束的更具体的子类型——也就是除了满足基础要求,还能拥有额外特性或更严格的类型定义。
举两个实际场景说明:
带额外属性的函数:
假设我们传入一个带有自定义属性的函数:const add = (x: number, y: number): number => x + y; add.version = "1.0"; // 给函数添加额外属性 const wrappedAdd = wrap(add);此时F会被实例化为
((x: number, y: number) => number) & { version: string },但你返回的(...a: Parameters<F>) => f(...a)只是一个普通的返回number的函数,完全没有version属性,自然无法匹配F的类型。参数类型更严格的函数:
再比如F可以是参数范围更窄的函数:type StrictAdd = (x: 1 | 2, y: 1 | 2) => number; const strictAdd: StrictAdd = (x, y) => x + y; const wrappedStrict = wrap(strictAdd);这里F的类型是
StrictAdd,虽然你返回的函数参数也是[1|2, 1|2]、返回值是number,但TypeScript认为StrictAdd是一个更具体的子类型,和你返回的通用函数类型并不完全等价,因此无法直接赋值。
简单来说,泛型约束只定义了F的“及格线”,但F可以是任何高于及格线的子类型,你返回的对象只达到了及格线,没法满足F可能存在的额外要求,所以会触发这个报错。
内容的提问来源于stack exchange,提问作者Hazel

