You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何包含特定属性时TypeScript泛型类型推断结果不同?

类型约束问题:用属性返回类型约束另一属性的参数类型

问题代码

// 基础类型及继承它的另一个类型
interface Base { x: string; }
interface Foo extends Base { y: number; }

// 接收Foo作为参数的函数类型
type Fn<T> = (t: T) => void
declare const FooFn: Fn<Foo>

// 我希望利用'x'的返回类型逆变地约束'y'可分配的函数类型
type Baz = <R extends Base, F extends Fn<R>>(arg: {
  x: (bar: Foo) => R
  y?: F[]
}) => R;

// 注意:当尝试设置'y'时,FooFn会触发错误
// 尽管从'x'中能正确推断出R(见R1)
declare const baz: Baz;
const R1 = baz({ x: (ctx) => ctx })
const R2 = baz({ x: (ctx) => ctx, y: [FooFn] }) 

问题描述

我想要创建一个接收props对象的函数,其中x属性的返回类型要作为y属性参数的约束。但实际使用时,添加y属性会导致x的返回类型推断结果从Foo变为Base,进而引发FooFn无法赋值给y的错误。即使使用NoInfer修饰F也无法解决,我的目标是让y能接受处理x返回类型的函数数组。

原因分析

当前泛型定义<R extends Base, F extends Fn<R>>会让TypeScript同时从x和y两个属性推断R的类型。由于函数参数是逆变的,Fn<Foo>属于Fn<Base>的子类型,TypeScript会优先选择满足所有约束的最宽泛类型Base,而非从x本身推断出的Foo。这就导致x的返回类型被强制放宽,最终引发类型不匹配。

解决方案

需要让TypeScript先从x推断出R的类型,再用该类型约束y,可以去掉多余的泛型参数F,直接将y的类型与R关联:

interface Base { x: string; }
interface Foo extends Base { y: number; }

type Fn<T> = (t: T) => void
declare const FooFn: Fn<Foo>

// 修改后的Baz类型:先推断R,再约束y的函数类型
type Baz = <R extends Base>(arg: {
  x: (bar: Foo) => R,
  y?: Fn<R>[]
}) => R;

declare const baz: Baz;
const R1 = baz({ x: (ctx) => ctx }) // R1 类型为 Foo
const R2 = baz({ x: (ctx) => ctx, y: [FooFn] }) // 类型校验通过,R2 类型为 Foo

如果需要更严格的推断(防止R被反向放宽),可在TypeScript 5.4及以上版本中使用NoInfer确保y的类型不影响R的推断:

type Baz = <R extends Base>(arg: {
  x: (bar: Foo) => R,
  y?: Fn<NoInfer<R>>[]
}) => R;

这样修改后,TypeScript会优先基于x的返回值确定R,再用该类型校验y中的函数,既满足了约束要求,又保证了推断的准确性。

内容的提问来源于stack exchange,提问作者Sebastian Nemeth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:04:51