我想定义一个打字稿函数,其条件返回类型取决于参数的类型:使用 true 类型的参数调用它返回一个字符串,使用 false 类型的参数调用它返回一个数字。我想出了以下函数 foo() ,它几乎按预期工作:
但是当直接将函数调用分配给不兼容的类型时,不会抛出类型错误,这是为什么?
// 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'."
相同的代码
正如所写,似乎没有任何理由为什么
foo
是TReturn
中的generic,因为最好的情况是
TReturn
被指定为TSomeArg extends true ? string : number
,并且任何其他类型参数都会给你你不想要的行为。但这里的问题不是要避免不良行为,而是要理解为什么会发生这种情况。
主要原因是函数返回类型充当推理目标,如microsoft/TypeScript#16072中实现。当你写的时候
const testFive: number = foo(true);
您在
context中调用
foo(true)
,其返回类型预计为 number
。这是 TReturn
的推理站点。仅当调用函数时推理失败时,才使用通用类型参数默认值。但是推理成功了,所以上面的调用相当于
const testFive: number = foo<true, number>(true);
你会得到你不喜欢的行为。