我想定义一个具有条件返回类型的类型脚本函数,该类型取决于参数的类型:用true类型的参数调用,它返回一个字符串,用False类型的参数调用,它返回一个数字.我想出了以下函数foo(),它几乎按预期工作:

  • 用true调用它,就会推断出正确的返回类型(字符串)
  • 将其称为"假",则推断出正确的返回类型(数字)

但当直接将函数调用分配给不兼容的类型时,不会引发类型错误,为什么呢?

// generic function with conditional return-type TReturn that depends on TSomeArg
const foo = <
  TSomeArg extends boolean,
  TReturn = TSomeArg extends true ? string : number,
>(
  someArg: TSomeArg,
): TReturn => {
  // to return the conditional type TReturn, one needs to explictly assert it, since typescript will otherwise infer the union type: 'Some String'|1234
  return (someArg ? "Some String" : 1234) as unknown as TReturn;
};

// this works fine
const testOne = foo(true); // inferred string as expected: "const testOne: string"
//      ^?

const testTwo = foo(false); // inferred number as expected: "const testTwo: number"
//      ^?

const testThree: number = testOne; // throws Error as expected: "Type 'string' is not assignable to type 'number'."
const testFour: string = testTwo; // throws Error as expected: "Type 'number' is not assignable to type 'string'."

// BUT, here I would expect some type-errors
const testFive: number = foo(true); // unexpected: no type-error (string is inccorrectly accepted as a number)
const testSix: string = foo(false); // unexpected: no type-error (number is incorrectly accepted as a string)

// only when I explicitly define TSomeArg, then the type-errors are thrown as expected
const testSeven: number = foo<true>(true); // throws type-error as expected: "Type 'string' is not assignable to type 'number'."
const testEighth: string = foo<false>(false); // throws type-error as expected: "Type 'number' is not assignable to type 'string'."

相同代码为ts-playground

推荐答案

正如所写的那样,似乎没有任何理由说明fooTReturn中的generic,因为最好的情况是TReturn被指定为TSomeArg extends true ? string : number,并且任何其他类型参数都会给您带来不想要的行为. 但这里的问题不是要避免不良行为,而是要了解它为什么会发生.


主要原因是function return types serve as inference targets,正如microsoft/TypeScript#16072中所实施的那样. 当你写

const testFive: number = foo(true);

您正在context中调用foo(true),因此返回类型预计为number. 这作为TReturn的推断点. 只有在调用该函数时推理失败时才使用Generic type argument defaults. 但推理成功,所以上面的调用相当于

const testFive: number = foo<true, number>(true);

你就会得到你不喜欢的行为.

Typescript相关问答推荐

Typescript抱怨T可以用约束MyType的不同子类型实例化'

实现忽略了JSSOM中的doctor.body.innerHTML集

返回对象的TypScript构造函数隐式地替换This的值来替换super(.)的任何调用者?

如何从具有给定键列表的对象类型的联合中构造类型

TypeScript实用程序类型—至少需要一个属性的接口,某些指定属性除外

为什么TypeScript失go 了类型推断,默认为any?''

键集分页顺序/时间戳上的筛选器,精度不相同

来自类型脚本中可选对象的关联联合类型

如何在Angular 12中创建DisplayBlock组件?

访问继承接口的属性时在html中出错.角形

被推断为原始类型的交集的扩充类型的交集

分解对象时出现打字错误

react 路由Use RouteLoaderData类型脚本错误

我不明白使用打字脚本在模板中展开参考

编剧- Select 下拉框

如何为带有参数的函数类型指定类型保护?

有没有办法在Reaction组件中动态导入打字脚本`type`?

在ts中获取级联子K类型?

无法使用prisma中的事务更新记录

数据库中的表请求返回如下日期:2023-07-20T21:39:00.000Z 我需要将此数据格式化为 dd/MM/yyyy HH:mm