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

为何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', '♠'),
]);

具体原因分析

  1. TypeScript的结构类型判定:所有Card实例的类型都是Card——TypeScript不关心实例的运行时值,只看类型结构。即使两个Card实例的rank和suit不同,它们的类型在TypeScript眼中完全一致。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:33:28