类型缩小在通用约束下无法按预期工作

Type Narrowing not working as expected with generic constraints

我遇到过这样一种情况,泛型类型受联合类型约束,但我发现这样做不会使类型变窄以按预期工作。下面的代码片段显示了正在发生的事情。

function somefunc<T extends string | number>(input: T): T {
  if (typeof input === "string") {
    // expecting input to be of type "string"
    // but input becomes of type T & "string"
    input
  } else {
    // expecting input to be of type "number"
    // but input becomes of type T extends string | number
    input
 }
}

如果我取消泛型并仅将函数参数注释为 string | number 它有效,但对于我的用例,我需要具有泛型约束。

编辑

该用例基本上是尝试将其与条件类型一起使用。基本上我想让结果类型成为取决于输入类型的条件类型。所以当输入类型为number时,结果也为number,当输入为string时,结果也为string。基本上是这样的:

type Result<T> = T extends string ? string : number

function somefunc<T extends string | number>(input: T): Result<T> {
  if (typeof input === "string") {
    // expecting input to be of type "string"
    // but input becomes of type T & "string"
    input
  } else {
    // expecting input to be of type "number"
    // but input becomes of type T extends string | number
    input
 }
}
 

我可能遗漏了一些东西,但问题是,我如何拥有基于联合的泛型约束并按照我的预期进行类型缩小工作。在上面的代码中,这意味着,在 if 分支中,input 变为类型 string,而在 else 分支中,它变为 number(或在至少变成 T & number)

** 编辑 **

我能够使用函数重载实现我想要的。我只是想知道是否可以使用泛型和条件类型来实现同样的事情。

如果您想缩小到精确类型,请考虑下一个示例:

function somefunc(input: number): number
function somefunc(input: string): string
function somefunc(input: string | number): string | number {
  if (typeof input === "string") {
    return input // stirng
  } else {
    return input // number
  }
}


const x = somefunc(10)

缺点:没有泛型)

未缩小的原因在

中有解释

正确缩小类型的 Hacky 方法:

type Result<T> = T extends string ? string : number;

function somefunc<T extends string | number>(input: T): Result<T> {
  const inputNarrowed: string | number = input;

  if (typeof inputNarrowed === "string") {
    inputNarrowed; // string
  } else {
    inputNarrowed; // number
  }

  return inputNarrowed as Result<T>;
}

具有重载 + 条件泛型的替代解决方案(我更喜欢)

type Result<T> = T extends string ? string : number;

function somefunc<T extends string | number>(input: T): Result<T>;
function somefunc(input: string | number) {
  if (typeof input === "string") {
    input; // string
  } else {
    input; // number
  }

  return input;
}

const str = somefunc("string"); // string
const num = somefunc(1); // number