简体   繁体   English

泛型类型扩展联合不会被类型保护缩小

[英]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!";
  }
}

Playground Link 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM