简体   繁体   English

TypeScript `is` 类型谓词(用户定义的类型保护函数)来实现幂等对象类型

[英]TypeScript `is` type predicates (user-defined type guard functions) to implement idempotent object types

I am confused to take advantage of type predicates (user-defined type guard functions)我很困惑利用类型谓词(用户定义的类型保护函数)

As addressed in my earlier questions: How to fix the broken code with idempotent (self flatten) types in TypeScript?正如我之前提出的问题: 如何在 TypeScript 中使用幂等(自展平)类型修复损坏的代码? , my final goal is to implement a mappable object that has the map method( mp in the sample code to avoid confusion) that should work similarly as pipeline-operator (such as F# |> ). ,我的最终目标是实现一个具有映射方法(示例代码中的mp以避免混淆)的可映射对象,该方法应该与管道运算符(例如 F# |> )类似。

Since any objects and primitives in JavaScript are idempotent(self/auto flatten), Object(5) === Object(Object(5)) ;由于 JavaScript 中的任何对象和原语都是幂等的(self/auto flatten),因此Object(5) === Object(Object(5)) ; I try to implement idempotent object/ functor as P and mp at first.我首先尝试将幂等对象/函子实现为Pmp

I asked How to express idempotent (self flatten) types in TypeScript?我问如何在 TypeScript 中表达幂等(自展平)类型? ; ; : TTX = TX ; : TTX = TX ; however, the approach in the answer, unfortunately failed:然而,不幸的是,答案中的方法失败了:

the approach eventually failed这种方法最终失败了

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

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

I gave up this approach and now, and refactored my code to bind JS and TypeScript inference closely, and try to take advantage of is type predicates (user-defined type guard functions)我现在放弃了这种方法,并重构了我的代码以紧密绑定 JS 和 TypeScript 推理,并尝试利用is 类型谓词(用户定义的类型保护函数)

(Please note: This is the simplest code to fucus on the issue; the actual implementation should use Symbol for property to check self-type is instead of using mp property, also None needed ) (请注意:这是在这个问题上亲缘最简单的代码;实际执行应使用Symbol的属性来检查自我型is ,而不是使用mp特性,也None必要)

Alse see: TS playground另见: TS游乐场

Works fine inside of the implementation, but does not work in the test code在实现内部工作正常,但在测试代码中不起作用

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

在此处输入图片说明


在此处输入图片说明


在此处输入图片说明



No error version by disabling is通过禁用没有错误版本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>

在此处输入图片说明

Any ideas?有任何想法吗? I've been working on this more than a week.我已经为此工作了一个多星期。 Thanks!谢谢!

Ok, at least for this issue, I figured out...好吧,至少对于这个问题,我想通了......

The type inference of this kind is only available in the context of the definition, and for the outside of the context to react the input type, we need to write the conditional type:这种类型推断只能在定义的上下文中使用,而要让上下文外部对输入类型做出反应,我们需要编写条件类型:

  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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM