为何移除数组元素可消除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
相关产品推荐
相关产品推荐

