使用条件返回类型实现泛型函数 [英] Implementing a generic function with a conditional return type

查看:38
本文介绍了使用条件返回类型实现泛型函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试一个非常基本的(人为的)条件类型函数并遇到意外错误:

I'm trying a very basic (contrived) conditional type function and getting unexpected errors:

function test<
  T
>(
  maybeNumber: T
): T extends number ? number : string {
  if (typeof maybeNumber === 'number') {
    return maybeNumber // Type 'T & number' is not assignable to type 'T extends number ? number : string'.
  }

  return 'Not a number' // Type '"Not a number"' is not assignable to type 'T extends number ? number : string'.
}

我认为这是条件类型的非常简单的用法,所以不确定发生了什么.有什么想法吗?

I thought that this was a pretty straightforward usage of a conditional type so not sure what's going on. Any ideas?

澄清一下,我并不是真的想实现这个特定的功能.我只是在试验条件类型,想更好地理解为什么这实际上不起作用.

To clarify, I’m not really trying to implement this specific function. I’m just experimenting with conditional types and want to better understand why this doesn’t actually work.

推荐答案

根本问题是 TypeScript 的编译器没有通过控制流分析来缩小泛型类型变量的类型.当你检查 (typeof MaybeNumber === "number") 时,编译器可以将 value maybeNumber 缩小到 number>,但它不会将类型参数 T 缩小为number.因此它无法验证将 number 值分配给返回类型 T extends number 是否安全?数字:字符串.编译器将不得不执行一些它目前不做的分析,例如好吧,如果 typeof MaybeNumber === "number",我们从类型推断 TmaybeNumber 单独,然后在这个块中我们可以将 T 缩小到 number,因此我们应该返回一个 number extends 类型的值number ? number : string,又名,number".但这不会发生.

The underlying issue is that TypeScript's compiler does not narrow the type of a generic type variable via control flow analysis. When you check (typeof maybeNumber === "number"), the compiler can narrow the value maybeNumber to number, but it does not narrow the type parameter T to number. And therefore it cannot verify that it's safe to assign a number value to the return type T extends number ? number : string. The compiler would have to perform some analysis it currently does not do, such as "okay, if typeof maybeNumber === "number", and we inferred T from the type of maybeNumber alone, then inside this block we can narrow T to number, and therefore we should return a value of type number extends number ? number : string, a.k.a., number". But this doesn't happen.

对于具有条件返回类型的泛型函数来说,这是一个相当大的痛点.关于此的规范开放 GitHub 问题可能是 microsoft/TypeScript#33912,但有一个其他一些 GitHub 问题,这是主要问题.

This is quite a pain point with generic functions with conditional return types. The canonical open GitHub issue about this is probably microsoft/TypeScript#33912, but there are a bunch of other GitHub issues out there where this is the main problem.

这就是为什么这行不通"的答案?

So that's the answer to "why doesn't this work"?

如果您对重构以使其正常工作不感兴趣,您可以忽略其余部分,但知道在这种情况下该做什么而不是等待语言改变可能仍然具有指导意义.

If you're not interested in refactoring to get this to work, you can ignore the rest, but it might still be instructive to know what to do in this situation instead of waiting for the language to change.

这里维护您的调用签名的最直接的解决方法是使您的函数成为单一签名重载 其中实现签名不是通用的.这实质上放松了实现内部的类型安全保证:

The most straightforward workaround here that maintains your call signature is to make your function a single signature overload where the implementation signature is not generic. This essentially loosens the type safety guarantees inside the implementation:

type MyConditional<T> = T extends number ? number : string;
type Unknown = string | number | boolean | {} | null | undefined;

function test<T>(maybeNumber: T): MyConditional<T>;
function test(maybeNumber: Unknown): MyConditional<Unknown> {
  if (typeof maybeNumber === 'number') {
    const ret: MyConditional<typeof maybeNumber> = maybeNumber;
    return ret;
  }
  const ret: MyConditional<typeof maybeNumber> = "Not a number";
  return ret;
}

在这里,我尽可能地尝试保证类型安全,方法是使用一个临时的 ret 变量,注释为 MyConditional使用控制流分析缩小类型的 maybeNumber.如果您切换检查,这至少会抱怨(将 === 转换为 !== 进行验证).但通常我只是做一些像这样更简单的事情,让筹码掉到他们可能的地方:

Here I've gone about as far as I can go to try to guarantee type safety, by using a temporary ret variable annotated as MyConditional<typeof maybeNumber> which uses the control-flow-analysis-narrowed type of maybeNumber. This will at least complain if you switch around the check (turn === into !== to verify). But usually I just do something simpler like this and let the chips fall where they may:

function test2<T>(maybeNumber: T): MyConditional<T>;
function test2(maybeNumber: any): string | number {
  if (typeof maybeNumber === 'number') {
    return maybeNumber;
  }
  return "Not a number";
}

好的,希望有帮助;祝你好运!

Okay, hope that helps; good luck!

游乐场链接代码

这篇关于使用条件返回类型实现泛型函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆