TypeScript泛型未展开,类型参数不可比报错问题解析
我正在处理一套有两种变体的数据结构:用于输出的完整版本,以及中间处理时部分属性可为空的不完整版本。大部分函数期望接收完整版本的类型,少数函数可以兼容两种类型。因为这是包含大量判别式联合的复杂树形结构,我没有把不完整变体定义成联合类型,而是用泛型参数实现,核心代码如下:
interface Data<Mode = 'DONE'> { someProperty: Mode extends 'BUILD' ? string | null : string; } type FullData = Data<'DONE'>; // 或直接使用默认值`Data` type TempData = Data<'BUILD'>;
部分函数需要先检查数据是否完整,再调用期望完整数据的函数:
function example(doneVal: Data<'DONE'>) {} function caller(buildVal: Data<'BUILD'>) { if (buildVal.someProperty != null) { example(buildVal); } }
这段代码会触发报错:
Argument of type 'Data<"BUILD">' is not assignable to parameter of type 'Data<"DONE">'.
Type '"BUILD"' is not assignable to type '"DONE"'. (ts2345)
我理解TypeScript没办法通过someProperty != null的检查自动推断buildVal的类型,但就算用类型断言:
example(buildVal as Data<'DONE'>);
还是会报错:
Conversion of type 'Data<"BUILD">' to type 'Data<"DONE">' may be a mistake because neither type sufficiently overlaps with the other.
Type '"BUILD"' is not comparable to type '"DONE"'
这是怎么回事?看起来TypeScript并没有按结构比较类型,而是在按泛型参数的字面量值比较?
如果我不使用单一泛型类型,而是分别写出两种变体的类型,代码就能正常运行。
我可以用类型守卫解决这个问题(调用时不用显式断言类型),但还是想知道背后的原因。目前我的实验显示,把类型条件里的Mode extends 'BUILD'改成'BUILD' extends Mode时,报错信息会变化;而且默认参数类型(Data<Mode>或Data<Mode extends 'DONE' | 'BUILD'>)不影响结果。
补充:进一步实验后发现,用type TempData = Data<'BUILD'> | Data<'DONE'>或Data<'BUILD' | 'DONE'>可以实现类型转换,但我不喜欢这种冗余写法,还是想知道原方案失效的原因。
内容的提问来源于stack exchange,提问作者Bergi

