[英]Generic type extending union is not narrowed by type guard
I tried to replicate Anders' example for conditional types and generics which he showed at Build 2018 (36:45).我试图复制 Anders 的条件类型示例和 generics,他在Build 2018 (36:45) 中展示了这些示例。 He uses a conditional type as a return type as replacement for more traditional function overloads.
他使用条件类型作为返回类型来替代更传统的 function 重载。
The slide has the following:幻灯片有以下内容:
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>
I tried to simplify this a bit and came up with the following example.我试图简化一下,并提出了以下示例。 The conditional type returns
number
when given a string
and vice versa, while the function implements this conditional type as return type.条件类型在给定
string
时返回number
,反之亦然,而 function 将此条件类型实现为返回类型。
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
However both return statements show the same error:但是,两个返回语句都显示相同的错误:
Type '42' is not assignable to type 'Return<T>'.(2322)
Type '"Hello World!"' is not assignable to type 'Return<T>'.(2322)
What am I missing here?我在这里想念什么?
Here's why it doesn't work: Typescript does control-flow type narrowing on regular variables, but not on type variables like your T
.这就是它不起作用的原因:Typescript 对常规变量进行控制流类型缩小,但对像
T
这样的类型变量没有。 The type guard typeof x === "string"
can be used to narrow the variable x
to type string
, but it cannot narrow T
to be string
, and does not try.类型保护
typeof x === "string"
可用于将变量x
缩小为string
类型,但不能将T
缩小为string
,并且不尝试。
This makes sense, because T
could be the union type string | number
这是有道理的,因为
T
可能是联合类型string | number
string | number
even when x
is a string, so it would be unsound to narrow T
itself, or to narrow T
's upper bound. string | number
即使x
是一个字符串,所以缩小T
本身或缩小T
的上限是不合理的。 In theory it would be sound to narrow T
to something like "a type which extends string | number
but whose intersection with string
is not never
" , but that would add an awful lot of complexity to the type system for relatively little gain.从理论上讲,将
T
缩小为“一种扩展string | number
但与string
的交集不是never
”之类的东西是合理的,但这会给类型系统增加非常多的复杂性,而增益相对较小。 There is no fully general way around this except to use type assertions;除了使用类型断言之外,没有完全通用的方法。 for example, in your code,
return 42 as Return<T>;
例如,在您的代码中,
return 42 as Return<T>;
. .
That said, in your use-case you don't need a generic function at all;也就是说,在您的用例中,您根本不需要通用函数; you can just write two 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!";
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.