You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何移除数组元素可消除TypeScript类型错误?

技术求助:TypeScript虚拟HTML元素类型推断异常

这是开发编辑器时创建虚拟HTML元素项目的一部分,我精简出以下代码片段。修改其中诸多内容(甚至移除一个hChildren元素)都能消除类型错误。

代码片段

const elements = ["h1", "h2", "h3", "h4", "button"] as const;

const hChildren = [
  "Welcome to Our Website!",
  "Hello, World!",
  "Discover More",
  "Explore Our Services",
  "Get Started Today",
  "About Us",
  "Contact Us",
] as const;

const buttonChildren = ["Browse"] as const;

function getRandomValue<T extends readonly string[]>(arr: T): T[number] {
  const randomIndex = Math.floor(Math.random() * arr.length);
  return arr[randomIndex];
}

const thisWorksAsExpected = (tagName: TagName) => {
  if (tagName === "h1" || tagName === "h2" || tagName === "h3") {
    return {
      tagName,
      children: getRandomValue(hChildren),
    };
  }

  return {
    tagName,
  };
};

const thisDoesntWorkAsExpected = (tagName: TagName) => {
  if (
    tagName === "h1" ||
    tagName === "h2" ||
    tagName === "h3" ||
    tagName === "h4"
  ) {
    return {
      tagName,
      children: getRandomValue(hChildren),
    };
  }

  return {
    tagName,
  };
};

type TagName = (typeof elements)[number];

type BaseElement<T extends TagName, C extends string> = {
  tagName: T;
  children?: C;
};

type VirtualElement =
  | BaseElement<"h1", (typeof hChildren)[number]>
  | BaseElement<"h2", (typeof hChildren)[number]>
  | BaseElement<"h3", (typeof hChildren)[number]>
  | BaseElement<"h4", (typeof hChildren)[number]>
  | BaseElement<"button", (typeof buttonChildren)[number]>;

elements.forEach((tagName) => {
  const builtVirtualElement1: VirtualElement = thisWorksAsExpected(tagName);
  console.log(builtVirtualElement1);
  const builtVirtualElement2: VirtualElement = thisDoesntWorkAsExpected(tagName);
  console.log(builtVirtualElement2);
});

问题描述

thisWorksAsExpected函数仅处理h1/h2/h3时能正常通过类型检查,但thisDoesntWorkAsExpected函数把h4也加入条件分支后,返回值无法匹配VirtualElement类型,触发类型错误。

解决方案

方案1:显式指定函数返回类型

直接给thisDoesntWorkAsExpected指定返回类型为VirtualElement,让TypeScript明确返回值的结构:

const thisDoesntWorkAsExpected = (tagName: TagName): VirtualElement => {
  if (tagName === "h1" || tagName === "h2" || tagName === "h3" || tagName === "h4") {
    return {
      tagName,
      children: getRandomValue(hChildren),
    };
  }

  return {
    tagName,
  };
};

方案2:使用类型守卫优化推断

自定义类型守卫函数,帮助TypeScript精确识别分支中的标签类型:

function isHeadingTag(tagName: TagName): tagName is "h1" | "h2" | "h3" | "h4" {
  return ["h1", "h2", "h3", "h4"].includes(tagName);
}

const thisDoesntWorkAsExpected = (tagName: TagName) => {
  if (isHeadingTag(tagName)) {
    return {
      tagName,
      children: getRandomValue(hChildren),
    };
  }

  return {
    tagName,
  };
};

原因分析

TypeScript的类型推断在处理联合类型分支时,当分支判断的联合成员数量超过3个,会触发联合类型收缩限制,无法精确推断每个分支的返回类型,导致返回值被拓宽为{ tagName: TagName; children?: string },无法匹配VirtualElement的精确联合类型结构。显式指定返回类型或使用类型守卫可以绕过这个限制,让TypeScript正确识别类型。

内容的提问来源于stack exchange,提问作者Nick Manning

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:18:10