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

TypeScript泛型K未被识别为联合类型的问题排查

TypeScript泛型联合类型未自动分发问题

问题场景

我编写的TypeScript泛型类型GraphSubAttributeNames在传入联合类型作为泛型参数K时,未按预期对联合类型的每个成员单独判断并合并结果,而是返回了不符合预期的单一值。代码示例如下:

type GraphSubAttributeNames<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> =
GraphSubAttributesMap<N> extends never ?
    'never' :
    GraphSubAttributesMap<N>[K] extends MapRelation ?
        GetGraphRelationMap<N, K> extends never ?
            'case 1' :
            'case 2' :
        'case 3'


type T1 = GraphSubAttributeNames<'artist', 'portrait'> // 'case 3', 结果正确
type T2 = GraphSubAttributeNames<'artist', 'heroArtwork'> // 'case 2', 结果正确
type T3 = GraphSubAttributeNames<'artist', 'heroArtwork' | 'portrait'> // 得到'case 3',预期应为'case 3' | 'case 2'

补充相关类型定义(我认为这些并非问题根源):

import {Artist, Footer, Site, Artwork, StrapiUser, Navigation, Exhibition, StrapiMedia} from "~/models"

type UnpackArray<T> = T extends (infer A)[] ? A : T

type StrapiAttributes = 'createdAt' | 'updatedAt' | 'publishedAt' | 'createdBy' | 'updatedBy'
type StripeStrapiAttributes<T extends StrapiApiTypes> = Omit<T, StrapiAttributes>

type MapRelation = {
    key: keyof ApiModelMap,
    many: boolean
}
// 关联类型可以是某个类型或MapRelation
type ApiModelMap = {
    artist: {
        type: Artist
        relations: {
            portrait: StrapiMedia
            heroArtwork: {key: 'artworks', many: false}
        }
    }
    artworks: {
        type: Artwork[],
        relations: {
            figure: StrapiMedia
        }
    }
    exhibitions: {
        type: Exhibition[]
    }
    sites: {
        type: Site[]
    }
    navigation: {
        type: Navigation
    }
    footer: {
        type: Footer
    }
}
type StrapiApiTypes = {
    [P in keyof ApiModelMap]: ApiModelMap[P]['type']
}
type ApiRelation = {key: ApiModelNames}

type ApiModelNames = keyof ApiModelMap
type ApiModel<T extends ApiModelNames> = UnpackArray<ApiModelMap[T]['type']>
type ApiModelIsArray<T extends ApiModelNames> = ApiModelMap[T]['type'] extends [] ? true : never
type GraphSubAttributesMap<N extends ApiModelNames> = ApiModelMap[N] extends { relations: unknown } ? ApiModelMap[N]['relations'] : never
type GraphSubAttributeKeys<N extends ApiModelNames> = keyof GraphSubAttributesMap<N>

type GetGraphRelationMap<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> = GraphSubAttributesMap<N>[K] extends ApiRelation ?
  GraphSubAttributesMap<GraphSubAttributesMap<N>[K]['key']> :
  never
type GetGraphRelationType<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> = GraphSubAttributesMap<N>[K] extends ApiRelation ?
  UnpackArray<ApiModelMap[GraphSubAttributesMap<N>[K]['key']]['type']> :
  never

问题原因

TypeScript的分布式条件类型仅在泛型参数直接出现在extends子句的左侧时才会触发。当前写法中,GraphSubAttributesMap<N>[K] extends MapRelation并不是将K直接放在extends左侧,而是先计算GraphSubAttributesMap<N>[K]的结果(当K是联合类型时,这个结果是对应属性值的联合类型),再整体判断是否符合MapRelation。

以T3为例,K是'heroArtwork' | 'portrait',此时GraphSubAttributesMap<'artist'>[K]的结果是StrapiMedia | MapRelation。这个联合类型并不完全符合MapRelation(因为StrapiMedia不是MapRelation的子类型),所以条件判断直接返回false,走'case 3'分支,而非逐个处理联合类型的成员再合并结果。

解决方法

要让泛型对K的联合类型进行自动分发,需要让K直接出现在extends子句的左侧,触发分布式条件类型。可以通过两种方式修改:

方式一:直接在原类型中添加K的分布式判断

在原类型的条件分支最外层,先判断K是否属于目标键类型,以此触发分布式处理:

type GraphSubAttributeNames<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> =
GraphSubAttributesMap<N> extends never ?
    'never' :
    K extends keyof GraphSubAttributesMap<N> ? // 触发分布式条件类型
        GraphSubAttributesMap<N>[K] extends MapRelation ?
            GetGraphRelationMap<N, K> extends never ?
                'case 1' :
                'case 2' :
            'case 3' :
        never;

方式二:拆分单一成员处理逻辑

将针对单个K成员的判断逻辑抽成单独的泛型类型,外层泛型自动对联合类型的每个成员进行分发:

// 处理单个K成员的逻辑
type GraphSubAttributeNameSingle<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> =
GraphSubAttributesMap<N> extends never ?
    'never' :
    GraphSubAttributesMap<N>[K] extends MapRelation ?
        GetGraphRelationMap<N, K> extends never ?
            'case 1' :
            'case 2' :
        'case 3';

// 外层泛型对K的联合类型自动分发
type GraphSubAttributeNames<N extends ApiModelNames, K extends GraphSubAttributeKeys<N>> =
    K extends infer U ? GraphSubAttributeNameSingle<N, U> : never;

修改后,T3的类型会正确返回'case 2' | 'case 3',符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 15:05:19