TypeScript报“无法调用可能为undefined的对象”错误原因分析
核心原因
问题出在TypeScript无法在泛型函数内部,根据泛型参数的条件类型结果,对config的类型进行准确窄化。
你的config类型是基于泛型A、B的条件类型:当A extends B时是maybeMutable(带可选mutate属性),否则是mutable(必选mutate属性)。但在函数内部,TypeScript没办法提前知道调用时A和B的具体关系,所以它会把config当成这两个类型的联合类型(maybeMutable<A,B> | mutable<A,B>)来处理。
当你用'mutate' in config判断时,TypeScript只能确定config存在mutate属性,但对于maybeMutable类型来说,可选属性允许显式设置为undefined——哪怕属性存在,TypeScript也无法排除它是undefined的可能。因此config.mutate的类型会被推断为((x:A)=>B) | undefined,赋值给mutate变量后,变量类型就变成了((x:A)=>B) | ((x:A)=>A) | undefined,自然会触发“Cannot invoke an object which is possibly 'undefined'”的错误。
可行解决方法
方案1:非空断言(简单直接)
在赋值config.mutate时加上非空断言!,明确告诉TypeScript这个值肯定不是undefined(前提是你能确保逻辑上不会出现mutate存在但为undefined的情况):
const myFunction = function<A,B>(config : A extends B ? maybeMutable<A,B> : mutable<A,B>, argument : A){ let mutate; if ('mutate' in config) { mutate = config.mutate!; } else { mutate = (x : A) => x } mutate(argument); }
方案2:添加类型标识,用区分联合类型
给两个类型增加一个共同的标识字段,让TypeScript能准确区分类型,从而完成可靠的窄化:
type mutable<A,B> = { type: 'mutable'; mutate: (x : A) => B } type maybeMutable<A,B> = { type: 'maybeMutable'; mutate? : (x : A) => B; } const myFunction = function<A,B>(config : A extends B ? maybeMutable<A,B> : mutable<A,B>, argument : A){ let mutate: (x:A) => A | B; if (config.type === 'mutable') { mutate = config.mutate; } else { mutate = config.mutate ?? ((x : A) => x); } mutate(argument); }
方案3:使用函数重载替代条件类型
直接为两种场景定义函数重载,让TypeScript根据参数类型自动匹配正确的处理逻辑:
type mutable<A,B> = { mutate: (x : A) => B } type maybeMutable<A,B> = { mutate? : (x : A) => B; } // 重载1:当A extends B时,接收maybeMutable类型的config function myFunction<A extends B, B>(config: maybeMutable<A,B>, argument: A): void; // 重载2:当A不extends B时,接收mutable类型的config function myFunction<A,B>(config: mutable<A,B>, argument: A): void; // 通用实现 function myFunction<A,B>(config: maybeMutable<A,B> | mutable<A,B>, argument: A) { // 使用空值合并运算符直接处理两种情况 const mutate = config.mutate ?? ((x:A) => x); mutate(argument); }
内容的提问来源于stack exchange,提问作者chilly_maximus

