首页 > 解决方案 > TypeScript `is` 类型谓词(用户定义的类型保护函数)来实现幂等对象类型

问题描述

我很困惑利用类型谓词(用户定义的类型保护函数)

正如我之前的问题所述:如何在 TypeScript 中使用幂等(自展平)类型修复损坏的代码?,我的最终目标是实现一个具有 map 方法的可映射对象(mp在示例代码中以避免混淆),它应该与管道操作符(例如 F# |>)类似地工作。

由于 JavaScript 中的任何对象和原语都是幂等的(self/auto flatten)Object(5) === Object(Object(5)),我首先尝试实现幂等对象/仿P函数mp

我问如何在 TypeScript 中表达幂等(自展平)类型?; : TTX = TX ; 但是,不幸的是,答案中的方法失败了:

这种方法最终失败了

type p<A> = {
  map: <B>(R: (a: A) => B) => P<B>
};

type P<A> =  //-------------idempotence
  A extends p<unknown>
  ? A
  : p<A>;

我现在放弃了这种方法,并重构了我的代码以紧密绑定 JS 和 TypeScript 推理,并尝试利用类型谓词(用户定义的类型保护函数)is

(请注意:这是最简单的代码来解决这个问题;实际的实现应该使用Symbolfor 属性来检查自身类型is而不是使用mp属性,也None需要)

另见:TS 游乐场

在实现内部工作正常,但在测试代码中不起作用

type P<A> = { mp: <B>(R: (a: A) => B) => P<B> };

const isP = <A,>(X: A | P<A>): X is A =>
  "mp" in X;
//is type predicates (user-defined type guard functions)

const P = <A,>(x: A) =>
  ((X: A | P<A>) =>
    isP(X)
      ? X // X: A //works fine as expected by isP
      : Object.defineProperty(X, //X: P<A> //works fine as expected
        "mp", { value: <B,>(f: (a: A) => B) => P(f(x)) })
  )(Object(x)); //idempotent

//--------------------------------
const f = (a: number) => a + 1;
//--------------------------------
const x = P(5); // 5 | P<5>  
                // P<5> expected or P<number> is ok
const xx = P(P(5)); // 5 | P<5> | P<5 | P<5>>
                    // P<5> expected  or P<number> is ok
const a = P(5).mp(f); //any
/* Property 'mp' does not exist on type '5 | P<5>'.
Property 'mp' does not exist on type '5'.ts(2339) */

在此处输入图像描述


在此处输入图像描述


在此处输入图像描述



通过禁用没有错误版本is

type P<A> = { mp: <B>(R: (a: A) => B) => P<B> };

const isP = <A,>(X: A | P<A>): X is A =>
  "mp" in X;

const P = <A,>(x: A) =>
  ((X: A | P<A>) =>
    isP(X)
      ? X as unknown as P<A> 
        //disabling isP by overriding as P<A> 
      : Object.defineProperty(X,
        "mp", { value: <B,>(f: (a: A) => B) => P(f(x)) })
  )(Object(x));

//--------------------------------
const f = (a: number) => a + 1;
//--------------------------------
const x = P(5); // P<number>
const xx = P(P(5)); // P<P<number>> 
// want this as P<number> as idempotence
const a = P(5).mp(f);// P<number>

在此处输入图像描述

有任何想法吗?我已经为此工作了一个多星期。谢谢!

标签: javascripttypescriptfunctional-programming

解决方案


好吧,至少对于这个问题,我想通了......

这种类型推断只能在定义的上下文中使用,而对于上下文外部对输入类型的反应,我们需要编写条件类型:

  type P<A> = {
    mp: <B>(R: (a: A) => B) => P<B>
  };

  const P = <A>(x: A) =>
    ((X: object) =>
      ("mp" in X
        ? X
        : Object.defineProperty(X,
          "mp", { value: <B>(f: (a: A) => B) => P(f(x)) })
      ) as (A extends { mp: unknown } ? A : P<A>)
    )(Object(x));

  //--------------------------------
  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>

  //this still has problem with type-error
  const compose =
  <A, B, C>(g: (b: B) => C) =>
    (f: (a: A) => B) =>
      (a: A) =>// g(f(a))
        P(a).mp(f).mp(g);

推荐阅读