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

为什么TypeScript会将可辨识联合收窄为any而非never?

问题描述

我在TypeScript中定义了一个带标签的可辨识联合(discriminated union)来表示多种输入类型。编写基于type字段(判别符)的穷尽switch辅助函数时,TypeScript错误地将默认分支的类型收窄为any,而非预期的never。

穷尽处理意味着已覆盖所有case,默认分支本应是不可达的断言,但此处TypeScript错误推断输入类型为any。以下是完整代码示例:

type inputType = {
    type: "select";
    source: "notes";
    folder: string;
} | {
    type: "tag";
    exclude?: string | undefined;
} | {
    type: "slider";
    min: number;
    max: number;
} | {
    type: "note";
    folder: string;
} | {
    type: "folder";
} | {
    type: "dataview";
    query: string;
} | {
    type: "number" | "text" | "date" | "time" | "datetime" | "textarea" | "toggle" | "email" | "tel";
} | {
    type: "select";
    source: "fixed";
    options: {
        value: string;
        label: string;
    }[];
} | {
    type: "multiselect";
    source: "notes";
    folder: string;
} | {
    type: "multiselect";
    source: "fixed";
    multi_select_options: string[];
    allowUnknownValues: boolean;
} | {
    type: "multiselect";
    source: "dataview";
    query: string;
    allowUnknownValues: boolean;
} | {
    type: "document_block";
    body: string;
}

function absurd<T>(_: never): T {
    throw new Error('Absurd!')
}

export function requiresListOfStrings(input: inputType): boolean {
    switch (input.type) {
        case "multiselect":
        case "tag":
            return true;
        case "select":
        case "dataview":
        case "note":
        case "folder":
        case "slider":
        case "document_block":
        case "number":
        case "text":
        case "date":
        case "time":
        case "datetime":
        case "textarea":
        case "toggle":
        case "email":
        case "tel":
            return false;
        default:
            return absurd(input.type); // 这里出现错误
    }
}

TypeScript报告的错误如下:

Argument of type 'any' is not assignable to parameter of type 'never'.
Property 'type' does not exist on type 'never'.

我知道可以通过将type属性提取到变量中来规避,但想了解错误产生的原因。已在5.4和5.5版本测试,均出现该问题。


问题原因解释

这个问题的核心在于TypeScript对嵌套在switch语句中的复杂可辨识联合类型的收窄逻辑存在局限性,具体原因如下:

  1. 重复标签的联合类型结构冲突
    你的inputType联合中存在多个共享同一type标签的成员:比如两个type: "select"的对象、三个type: "multiselect"的对象。当直接在switch中使用input.type时,TypeScript在处理这些重复标签的联合成员时,无法准确追踪所有可能的分支是否被完全覆盖。

  2. switch内的类型收窄边界限制
    在switch语句中,TypeScript是通过每个case的字面量匹配来逐步收窄input的类型。但对于存在重复type标签的联合类型,当处理完所有显式case后,TypeScript的类型检查器无法正确推断剩余的input.type应该是never,反而会因为内部的类型推断冲突,将其退化为any类型。

  3. 直接访问input.type的推断缺陷
    在default分支中,理论上input的类型应该被收窄为never(因为所有可能的type都已被case覆盖),但由于联合类型中重复标签的存在,TypeScript无法完成这个收窄,反而错误地判定input是never类型——而never类型上不存在任何属性,同时又因为内部推断矛盾,把input.type的类型判定为any,最终触发了两条错误信息。

而将type提取到变量中(比如const type = input.type;再基于这个变量写switch)可以规避问题,因为此时TypeScript会先将type推断为所有可能的字面量联合,之后对这个变量进行分支匹配时,能正确收窄类型,最终推断default分支的type为never。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:17:32