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

如何使用TypeScript的is类型谓词实现幂等(自扁平化)对象类型?

// 定义唯一标识,避免属性名冲突
const P_TAG = Symbol('P_TAG');

// 基础P类型定义
type PBase<A> = {
  [P_TAG]: true;
  mp: <B>(f: (a: A) => B) => P<B>;
};

// 幂等解包类型:递归把嵌套的P展开,保证P<P<A>> = P<A>
type UnwrapP<T> = T extends PBase<infer U> ? UnwrapP<U> : T;
type P<A> = PBase<UnwrapP<A>>;

// 类型守卫:判断是否是P类型,逻辑和判断条件匹配
const isP = <T>(x: unknown): x is P<T> => {
  return typeof x === 'object' && x !== null && P_TAG in x;
};

// P构造函数
function P<A>(x: A): P<A> {
  // 运行时幂等:如果已经是P类型直接返回
  if (isP(x)) return x as P<A>;
  // 非P类型包装
  const wrapped = Object(x) as any;
  wrapped[P_TAG] = true;
  wrapped.mp = <B>(f: (a: UnwrapP<A>) => B) => P(f(wrapped.valueOf()));
  return wrapped as P<A>;
}

// 测试用例
const f = (a: number) => a + 1;

const x = P(5); // 类型正确:P<number>
const xx = P(P(5)); // 类型正确:P<number> 满足幂等
const a = P(5).mp(f); // 类型正确:P<number>
const b = P(P(5)).mp(f).mp(f); // 链式调用正常,类型正确:P<number>

核心修正点

  1. 类型守卫逻辑修正:原代码的守卫把判断逻辑和返回的类型谓词写反了,携带标识属性的才是P类型,所以守卫返回值需要和判断条件匹配。
  2. 幂等类型实现:通过UnwrapP递归解包嵌套的P类型,只要传入的是P实例,就提取内部的原始类型,保证P<P<number>>最终解析为P<number>,完全符合幂等要求。
  3. 运行时和类型对齐:构造函数先判断传入值是否是P实例,是就直接返回,运行时满足Object(5) === Object(Object(5))的幂等特性,类型上也同步返回解包后的P类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:15:04