我试图复制安德斯在条件Build 2018(36:45)中显示的条件类型和泛型示例。他使用条件类型作为返回类型,以替代更传统的函数重载。
幻灯片具有以下内容:
type Name = { name: string };
type Id = { id: number };
type Check = { enabled: boolean };
type LabelForType<T> =
T extends string ? Name :
T extends number ? Id :
T extends boolean ? Check :
never;
declare function createLabel<T extends string | number | boolean>(value: T): LabelForType<T>
我试图简化一点,并提出了以下示例。当给定number
时,条件类型返回string
,反之亦然,而函数将这种条件类型实现为返回类型。
type Return<T> = T extends string ? number : T extends number ? string : never;
function typeSwitch<T extends string | number>(x: T): Return<T>{
if (typeof x == "string") {
return 42;
} else if (typeof x == "number") {
return "Hello World!";
}
throw new Error("Invalid input"); // needed because TS return analysis doesn't currently factor in complete control flow analysis
}
const x = typeSwitch("qwerty"); // number
但是两个return语句都显示相同的错误:
Type '42' is not assignable to type 'Return<T>'.(2322)
Type '"Hello World!"' is not assignable to type 'Return<T>'.(2322)
我在这里想念什么?
答案 0 :(得分:4)
这是为什么它不起作用的原因:Typescript对常规变量执行control-flow type narrowing,而对{em}类型变量却不起作用,如php artisan cache:clear
。类型防护T
可用于将变量typeof x === "string"
缩小为x
,但不能将string
缩小为T
,并且不会尝试。
这是有道理的,因为即使string
是字符串,T
仍可以是联合类型string | number
,因此缩小x
本身或缩小T
都是不合理的缩小T
的上限。从理论上讲,可以将T
缩小为“扩展string | number
但与string
的交点不是never
的类型 ,但这会为类型系统增加很多复杂性,而带来的收益却相对较少。除了使用类型断言,没有其他完全通用的方法。例如,在您的代码中,return 42 as Return<T>;
。
也就是说,在您的用例中,您根本不需要通用函数;您可以只写两个overload signatures:
// overload signatures
function typeSwitch(x: string): number;
function typeSwitch(x: number): string;
// implementation
function typeSwitch(x: string | number): string | number {
if (typeof x === "string") {
return 42;
} else {
// typeof x === "number" here
return "Hello World!";
}
}