为何UniqArray类型推断结果为never而非预期的T?
为什么UniqArray对不同Card实例推断为never?
你编写的TypeScript代码中,UniqArray本应约束数组元素唯一,但传入5个不同Card实例时,推断结果却为never,核心原因在于TypeScript的结构类型系统特性,以及你的类型工具对"重复"的判断逻辑:
// core export type HasDuplicate<T extends Array<unknown>> = T extends [ infer L, ...infer R, ] ? L extends R[number] ? true : HasDuplicate<R> : false; export type UniqArray<T> = HasDuplicate<T> extends false ? T : never; // game type CardRank = | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'T' | 'J' | 'Q' | 'K' | 'A'; type CardSuit = '♥' | '♦' | '♠' | '♣'; class Card { constructor( private readonly _rank: CardRank, private readonly _suit: CardSuit, ) {} get id() { return `${this._rank}:${this._suit}` as const; } } class Hand { constructor( private readonly _cards: UniqArray<[Card, Card, Card, Card, Card]>, ) {} } new Hand([ new Card('2', '♠'), new Card('3', '♠'), new Card('4', '♠'), new Card('5', '♠'), new Card('6', '♠'), ]);
具体原因分析
- TypeScript的结构类型判定:所有
Card实例的类型都是Card——TypeScript不关心实例的运行时值,只看类型结构。即使两个Card实例的rank和suit不同,它们的类型在TypeScript眼中完全一致。 - HasDuplicate的逻辑局限:
HasDuplicate是在类型层面检查元素是否重复。当传入[Card, Card, Card, Card, Card]时,L是Card类型,R[number]也是Card类型,L extends R[number]会被判定为true,因此HasDuplicate返回true,最终UniqArray返回never。
简单说:你想检查的是运行时实例值的唯一性,但HasDuplicate只能判断静态类型的重复性,两者不匹配导致了问题。
修正思路(类型层面实现卡片唯一性约束)
如果要在类型层面确保卡片不重复,需要让每个Card实例的类型带上自身的rank和suit字面量标识,让不同卡片的类型区分开:
// 修改Card类为泛型,绑定rank和suit的字面量类型 class Card<R extends CardRank, S extends CardSuit> { constructor( private readonly _rank: R, private readonly _suit: S, ) {} get id() { return `${this._rank}:${this._suit}` as const; } } class Hand<T extends readonly Card[]> { constructor( private readonly _cards: UniqArray<T> ) {} } // 此时每个Card实例的类型都是唯一的,UniqArray会正确推断 new Hand([ new Card('2', '♠'), new Card('3', '♠'), new Card('4', '♠'), new Card('5', '♠'), new Card('6', '♠'), ]);
这样每个Card实例的类型是Card<'2','♠'>、Card<'3','♠'>等不同的子类型,HasDuplicate会正确判断这些类型不重复,UniqArray就会返回原数组类型而非never。
内容的提问来源于stack exchange,提问作者Pavel Perevezencev
相关产品推荐
相关产品推荐

