javascript - 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
(请注意:这是最简单的代码来解决这个问题;实际的实现应该使用Symbol
for 属性来检查自身类型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>
有任何想法吗?我已经为此工作了一个多星期。谢谢!
解决方案
好吧,至少对于这个问题,我想通了......
这种类型推断只能在定义的上下文中使用,而对于上下文外部对输入类型的反应,我们需要编写条件类型:
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);
推荐阅读
- sql - 为什么不能计算字符串中的管道字符?
- python - 作为服务的 Python 脚本无法访问 asoundrc 配置文件
- php - 将多维数组与 PHP 中的另一个数组进行比较,并从多维数组中获取值
- javascript - 检查特定行是否存在
- angular - Typescript option. BaseUrl settings with nested directories
- saml - Keycloak User Attributes are empty after integration with Okta SAML Provider
- python - 将节点树表示为列表
- javascript - 浏览器在 vue.js 2 应用程序中加载网页时卡住了,但有时它会成功加载相同的代码
- java - “新字节['Ѐ']”中的'Ѐ'是什么意思?
- android - 无法创建 PushNotification 实例