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

时间:2020-03-01 13:08:05

标签: typescript typescript-generics

我试图复制安德斯在条件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)

我在这里想念什么?

1 个答案:

答案 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!";
  }
}

Playground Link