这是一个大项目的一部分,我基本上是为我正在制作的一种编辑器创建虚拟HTML元素.我花了很长时间试图缩小范围,我得到了下面的片段.更改这个代码段中的许多随机内容可以消除错误.即使删除hChildren中的一个也会删除错误.这是Playground link.我们将不胜感激.

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);
});

推荐答案

属性为union typesobject type通常不被认为等同于对象类型的联合,即使后者是必需的Cartesian product. 也就是说,如果你有{x: A | B, y: C | D},它不一定被视为等同于{x: A, y: C} | {x: A, y: D} | {x: B, y: C} | {x: B, y: D},即使一种类型的任何值也会是另一种类型的值. 主要原因是,即使是规模不大的unions ,也要分析这类类型.

但有足够多的人遇到了这个问题,并报告了它(例如,ms/TS#12052ms/TS#30170等),最终TS团队妥协并实现了所谓的smarter union type checkingmicrosoft/TypeScript#30779.折衷的办法是,如果笛卡尔乘积足够小,它将为您进行判断,并根据扩展成功或失败.如果它太大,那么它就放弃了,无论如何都不能进行类型判断.但"太大"是相当小的意思:

如果组合的数量超过了一个固定的限制(目前为25个),我们认为比较过于复杂,并确定"源"与"目标"不相关.

一旦可能的组合数达到26个或更多,它就会打破你看到的方式. 第Yes章这太混乱了 这就是为什么TypeScript通常不会做这样的事情. 参见this Twitter thread by the TS dev team lead,特别是this entry和后面的一个:

人们很容易说"哦,如果输入的数量是small,那么就使用暴力".事实上,TS在少数情况下会这样做.这样做很好,但会导致行为的不连续性,例如,15个值的联合与16个值的联合的行为不同.程序员特别讨厌这种事情,因为它 destruct 了可预测性.

这导致了令人困惑的开发人员和类似的堆栈溢出问题.哦,好吧!

Typescript相关问答推荐

类型{}上不存在属性&STORE&A;-MobX&A;REACT&A;TYPE脚本

如何重构长联合类型?

缩小并集子键后重新构造类型

错误TS2403:后续变量声明必须具有相同的类型.变量';CRYPTO';的类型必须是';CRYPATO';,但这里的类型是';CRYPATO';

react 路由Use RouteLoaderData类型脚本错误

路由链接始终返回到

在另一个类型的函数中,编译器不会推断模板文字类型

在打印脚本中使用泛型扩展抽象类

使用TypeScrip5强制使用方法参数类型的实验方法修饰符

Angular 自定义验证控件未获得表单值

在组件卸载时try 使用useEffect清除状态时发生冲突.react +打字

对于VUE3,错误XX不存在于从不存在的类型上意味着什么?

顶点堆叠的图表条形图未在正确的x轴上显示

如何为对象数组中的每个条目推断不同的泛型

显式推断对象为只读

递归JSON类型脚本不接受 node 值中的变量

如何确定剧作家中给定定位器的值?

带有 Select 器和映射器的Typescript 泛型

递归类型名称

可选字段和联合之间有什么区别吗?