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

TypeScript泛型参数推断异常:never类型错误排查

错误信息
TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
完整代码
type Equal<T1, T2> = T1 extends T2 ? (T2 extends T1 ? true : false) : false;
type AnyFunction = (...a: any) => any;
type Defined<Type> = Type extends undefined ? false : true;
type NotNull<Type> = Type extends undefined
  ? false
  : Type extends null
  ? false
  : true;
type ALotOf<T> = Record<any, T>;

type Constructor = { new (...args: any): any };
type ConstructorArray = Array<Constructor>;
type ConstructorLikeSimpleTypedProp =
  | ConstructorArray
  | Constructor
  | null
  | undefined;
type AnyOtherSimpleTypedProp = any;
type SimpleTypedProp = ConstructorLikeSimpleTypedProp | AnyOtherSimpleTypedProp;
type PropDefault = any;
type ComplexTypedProp = {
  type?: SimpleTypedProp;
  default?: PropDefault;
  required?: boolean | null;
};
type TypedProp = SimpleTypedProp | ComplexTypedProp;
type UntypedProp = string;

type TypedPropList = ALotOf<TypedProp>;
type UntypedPropList = Array<UntypedProp>;

type PropList = TypedPropList | UntypedPropList;
type ConstructorLikeSimpleTypedPropType<
  T extends ConstructorLikeSimpleTypedProp
> = T extends ConstructorArray
  ? InstanceType<T[number]>
  : T extends Constructor
  ? InstanceType<T>
  : any;
type AnyOtherSimpleTypedPropType<T extends AnyOtherSimpleTypedProp> = T;

type ValidAnyOtherDefault<
  Type extends SimpleTypedProp,
  Default extends AnyFunction
> = Type extends AnyOtherSimpleTypedProp
  ? Equal<ReturnType<Default>, AnyOtherSimpleTypedPropType<Type>> extends true
    ? Equal<Parameters<Default>, []>
    : false
  : false;
type ValidAnyFunctionDefault<
  Type extends SimpleTypedProp,
  Default extends AnyFunction
> = Type extends AnyFunction
  ? Equal<ReturnType<Default>, ReturnType<Type>> extends true
    ? Equal<Parameters<Default>, Parameters<Type>>
    : false
  : ValidAnyOtherDefault<Type, Default>;
type ValidConstructorLikeDefault<
  Type extends SimpleTypedProp,
  Default extends AnyFunction
> = Type extends ConstructorLikeSimpleTypedProp
  ? Equal<
      ReturnType<Default>,
      ConstructorLikeSimpleTypedPropType<Type>
    > extends true
    ? Equal<Parameters<Default>, []>
    : false
  : ValidAnyFunctionDefault<Type, Default>;
type ValidFunctionConstructorDefault<
  Type extends SimpleTypedProp,
  Default extends AnyFunction
> = Type extends FunctionConstructor
  ? true
  : ValidConstructorLikeDefault<Type, Default>;
type ValidTypeAndDefault<
  Type extends SimpleTypedProp | undefined,
  Default extends PropDefault | undefined
> = Defined<Default> extends true
  ? Default extends AnyFunction
    ? NotNull<Type> extends true
      ? ValidFunctionConstructorDefault<Type, Default>
      : Equal<Parameters<Default>, []>
    : false
  : true;
type ValidComplexTypedProp<T extends ComplexTypedProp> = ValidTypeAndDefault<
  T["type"],
  T["default"]
>;
type ValidTypedProp<T extends TypedProp> = T extends ComplexTypedProp
  ? ValidComplexTypedProp<T>
  : T extends SimpleTypedProp
  ? true
  : false;
type MarkEachTypedProp<P extends TypedPropList> = {
  [p in keyof P]: ValidTypedProp<P[p]>;
};
type ValidPropList<P extends PropList> = P extends UntypedPropList
  ? true
  : P extends TypedPropList
  ? false extends MarkEachTypedProp<P>[keyof MarkEachTypedProp<P>]
    ? false
    : true
  : false;
type TrueAnyFalseNever<T extends boolean> = T extends true ? any : never;
type NeverOrAny<T> = T extends never ? never : any;
function prop<P extends PropList>(
  ok: NeverOrAny<TrueAnyFalseNever<ValidPropList<P>>>,
  o: P
): P {
  return o;
}
export default prop(1, {
  A: {
    type: Boolean,
    required: true,
    default(): boolean {
      return true;
    },
  },
  B: {
    type: String,
    default(): string {
      return "hello";
    },
  },
  C: {
    type: String,
    default(): string {
      return "world";
    },
  },
  D: {
    type: String,
  },
  E: {
    type: String,
    required: true,
  },
});
问题描述

我期望prop()函数的第一个参数类型被推断为any,但实际却得到never类型,导致出现上述TS2345错误。

我想要实现TypeScript中的“自约束”功能:让TypeScript根据函数参数推断泛型类型,同时要求该泛型满足基于自身的约束规则——具体来说:

  • 当ComplexTypedProp同时存在type(Constructor类型)和default函数时,default函数的返回值必须匹配type对应的InstanceType;
  • 不同字段的type可以是不同的Constructor类型。

由于无法直接用简单类型(如Record<any, Type<T> | TypeAndDefault<T>>)表达该规则(T会被强制统一为同一类型),我构建了一个检查器类型来验证推断出的泛型是否符合规则:若符合则检查器类型为any,否则为never。

prop()函数的第一个参数使用该检查器类型,第二个参数为泛型P。若P符合规则,第一个参数可传入任意值;若不符合则参数类型变为never,触发编译错误以提示用户。

经测试,MarkEachTypedProp<P>能正确将o参数的所有顶级字段映射为true类型,但ValidPropList中的条件判断false extends MarkEachTypedProp<P>[keyof MarkEachTypedProp<P>] ? false : true被推断为false,进而导致第一个参数类型变为never。将该条件中的false改为true时,错误消失。请求排查该条件判断异常的原因。

问题原因与解决方法

问题出在ValidPropList的条件判断逻辑上:false extends MarkEachTypedProp<P>[keyof MarkEachTypedProp<P>] 的语义是检查false是否是所有属性类型的子类型,但你实际需要的是检查是否存在任何一个属性类型是false。

在TypeScript中,T[keyof T]会生成所有属性类型的联合类型。当使用X extends Y且Y是联合类型时,只有X是联合类型中每一个成员的子类型时,结果才为true。所以当MarkEachTypedProp<P>的所有属性都是true时,false extends true的结果是false,这就导致ValidPropList返回false,进而触发TrueAnyFalseNever返回never。

正确的逻辑应该是检查联合类型中是否全为true,可以通过MarkEachTypedProp<P>[keyof MarkEachTypedProp<P>] extends true ? true : false实现——如果所有属性类型都能赋值给true(即没有false),则返回true,否则返回false。

修改后的ValidPropList类型如下:

type ValidPropList<P extends PropList> = P extends UntypedPropList
  ? true
  : P extends TypedPropList
  ? MarkEachTypedProp<P>[keyof MarkEachTypedProp<P>] extends true
    ? true
    : false
  : false;

修改后,当MarkEachTypedProp<P>的所有属性都是true时,ValidPropList返回true,第一个参数类型变为any,即可正常传入数值1。

内容的提问来源于stack exchange,提问作者Tan Telephone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 13:05:27