为什么打字稿不能根据传递的参数正确推断 return 类型?

Why isn't typescript inferring return type correctly based on params passed?

我有这个功能,有一个 switch case,returns 两个不同的结果基于 case。

export function getTimeAgo(
  date: Date | string,
  mode: "days" | "minutes" = "days"
) {
  if (typeof date === "string") {
    date = new Date(date);
  }
  switch (mode) {
    case "days":
      return getTimeAgoByDays(date);
    case "minutes":
      return getTimeAgoByMinutes(date);
    default:
      return { timeAgo: "", numFormat: null };
  }
}

getTimeAgoByDays函数:

function getTimeAgoByDays(date: Date) {
  let timeAgo = "";
  const curDate = new Date();
  const days = Math.round(
    (date.valueOf() - curDate.valueOf()) / (1000 * 60 * 60 * 24)
  );

  if (days === 0) {
    timeAgo = "Ends today";
  } else if (days < 0) {
    const nonNegativeDays = days * -1;
    timeAgo = `${nonNegativeDays} days behind`;
  } else {
    timeAgo = `${days} days left`;
  }
  if (days === 1) {
    timeAgo = timeAgo.replace("days", "day");
  }

  return { timeAgo, numFormat: days };
}

然后调用它得到结果:

const { timeAgo, numFormat: days } = getTimeAgo(date,"days");

days 变量中,我收到一条错误消息,指出它可能是 null。但是基于modedays,结果不应该总是非null吗?

TypyScript 不会根据输入值和函数内部的代码进行任何控制流分析来确定函数的 return 类型。当您将鼠标悬停在 getTimeAgo 的定义上时,您会看到 return 类型只是函数 returned.

中所有不同事物的联合体
{
    timeAgo: string;
    numFormat: number;
} | {
    timeAgo: string;
    numFormat: null;
}

因此,无论您将什么传递给函数,return 类型将始终是此联合,其中 numFormat 可能是 null

您可以通过函数重载更改此行为。在这里,您明确告诉 TypeScript return 类型将基于参数。

export function getTimeAgo<
  T extends "days" | "minutes"
>(date: Date | string, mode: T): { 
  timeAgo: string, numFormat: T extends "days" | "minutes" ? number : null 
}

export function getTimeAgo(
  date: Date | string,
  mode: "days" | "minutes" = "days"
) {
  /* ... */
}

在return类型中,我们可以根据T创建条件,并相应地形成拟合return类型。

Playground

但在您提供的代码中,这似乎不是必需的。您也可以只删除 switch 语句的 default 子句,因为它不可访问。