为何将条件返回类型为string的函数赋值给number无类型错误?
我想定义一个TypeScript函数,让它的返回类型根据参数类型动态变化:传入true类型参数时返回string,传入false类型参数时返回number。我写的foo()函数大部分符合预期——调用foo(true)时能正确推断返回类型为string,调用foo(false)时能正确推断返回类型为number。但直接把函数调用结果赋值给不兼容类型时,TypeScript却没抛出类型错误,这是为什么?
// 带有依赖TSomeArg的条件返回类型TReturn的泛型函数 const foo = < TSomeArg extends boolean, TReturn = TSomeArg extends true ? string : number, >( someArg: TSomeArg, ): TReturn => { // 需显式断言才能返回条件类型TReturn,否则TypeScript会推断为联合类型'Some String'|1234 return (someArg ? "Some String" : 1234) as unknown as TReturn; }; // 以下情况正常 const testOne = foo(true); // 如预期推断为string:"const testOne: string" // ^? const testTwo = foo(false); // 如预期推断为number:"const testTwo: number" // ^? const testThree: number = testOne; // 如预期抛出错误:"Type 'string' is not assignable to type 'number'." const testFour: string = testTwo; // 如预期抛出错误:"Type 'number' is not assignable to type 'string'." // 但以下情况我预期会有类型错误 const testFive: number = foo(true); // 意外:无类型错误(string被错误地接受为number) const testSix: string = foo(false); // 意外:无类型错误(number被错误地接受为string) // 仅当显式定义TSomeArg时,才会如预期抛出类型错误 const testSeven: number = foo<true>(true); // 如预期抛出类型错误:"Type 'string' is not assignable to type 'number'." const testEighth: string = foo<false>(false); // 如预期抛出类型错误:"Type 'number' is not assignable to type 'string'."
核心原因是TypeScript的泛型类型推断逻辑:当你把foo(true)直接赋值给number类型变量时,TypeScript不会固定TSomeArg为true,而是优先满足赋值目标的兼容性——它会反向推断,寻找一个符合TSomeArg extends boolean的类型,使得TSomeArg extends true ? string : number匹配number。
显然,当TSomeArg被推断为false时,返回类型就是number,而true本身属于boolean类型,符合TSomeArg extends boolean的约束,因此TypeScript认为这个推断合法,自然不会报错。
而当显式指定foo<true>(true)时,TSomeArg被固定为true,返回类型锁定为string,此时赋值给number类型变量就会触发类型不兼容的错误。
不需要单独定义TReturn泛型参数,直接把条件类型作为函数返回类型即可。这样TypeScript会严格根据传入的参数类型推导返回类型,不会为适配赋值目标而改变泛型推断结果:
const foo = <TSomeArg extends boolean>(someArg: TSomeArg): TSomeArg extends true ? string : number => { return (someArg ? "Some String" : 1234) as unknown as TSomeArg extends true ? string : number; }; // 此时以下代码会如预期抛出错误 const testFive: number = foo(true); // 错误:Type 'string' is not assignable to type 'number' const testSix: string = foo(false); // 错误:Type 'number' is not assignable to type 'string'
内容的提问来源于stack exchange,提问作者jimmyorpheus

