Complex typing of a factory function returning a wrapper around a function in Typescript (generics of generics) - Stack Overflow

I want to create a factory function that takes an onOk and onErr handlers and return a wrapper around f

I want to create a factory function that takes an onOk and onErr handlers and return a wrapper around function that may throw.

const mayThrow = (fail: boolean) => {
   if (fail) {
      throw new Error('failed')
   }
   return 'ok'
}

Let's define both handler and their types

type Ok<T> = { ok: true, value: T }
type Err<E> = { ok: false, error: E }
type Result<T, E> = Ok<T> | Err<E>
const onOk = <T>(value: T): Ok<T> => ({ ok: true, value })
const onErr = <E>(error: E): Err<E> => ({ ok: false, error })

I have this factory function that I can almost type correctly.

function createSafeFactory<
   U extends (...args: any[]) => any,
   F extends (...args: any[]) => any
>(onOk: U, onErr: F) {
   return <A extends any[], T, E>(
      fn: (...args: A) => T,
      errorFn?: (error: unknown) => E
   ) => {
       return (...args: A): ReturnType<U> | ReturnType<F> => {
           try {
               const result = fn(...args)
               return onOk(result)
           } catch (e) {
               const error = errorFn ? errorFn(e) : (e as E)
               return onErr(error)
           }
       }
   }
}

const safe = createSafeFactory(onOk, onErr)
// const safe: <A extends any[], T, E>(fn: (...args: A) => T, errorFn?: ((error: unknown) => E) | undefined) => (...args: A) => Ok<unknown> | Err<unknown>

const safeFn = safe(mayThrow)
// const safeFn: (fail: boolean) => Ok<unknown> | Err<unknown>

const res = safeFn(true)
// const res: Ok<unknown> | Err<unknown>

if (res.ok) {
   const data = res.value
   // const data: unknown
}

data is unknown instead of string

What I want is the result to be typed Result<string, never> or Ok<string> | Err<never>.

The problem is here: ReturnType<U> | ReturnType<F>. I want the return type of U and F given that U and F are typed relative to T and E.

How can I achieve something like ReturnType<U<T>> | ReturnType<F<E>>

I want to create a factory function that takes an onOk and onErr handlers and return a wrapper around function that may throw.

const mayThrow = (fail: boolean) => {
   if (fail) {
      throw new Error('failed')
   }
   return 'ok'
}

Let's define both handler and their types

type Ok<T> = { ok: true, value: T }
type Err<E> = { ok: false, error: E }
type Result<T, E> = Ok<T> | Err<E>
const onOk = <T>(value: T): Ok<T> => ({ ok: true, value })
const onErr = <E>(error: E): Err<E> => ({ ok: false, error })

I have this factory function that I can almost type correctly.

function createSafeFactory<
   U extends (...args: any[]) => any,
   F extends (...args: any[]) => any
>(onOk: U, onErr: F) {
   return <A extends any[], T, E>(
      fn: (...args: A) => T,
      errorFn?: (error: unknown) => E
   ) => {
       return (...args: A): ReturnType<U> | ReturnType<F> => {
           try {
               const result = fn(...args)
               return onOk(result)
           } catch (e) {
               const error = errorFn ? errorFn(e) : (e as E)
               return onErr(error)
           }
       }
   }
}

const safe = createSafeFactory(onOk, onErr)
// const safe: <A extends any[], T, E>(fn: (...args: A) => T, errorFn?: ((error: unknown) => E) | undefined) => (...args: A) => Ok<unknown> | Err<unknown>

const safeFn = safe(mayThrow)
// const safeFn: (fail: boolean) => Ok<unknown> | Err<unknown>

const res = safeFn(true)
// const res: Ok<unknown> | Err<unknown>

if (res.ok) {
   const data = res.value
   // const data: unknown
}

data is unknown instead of string

What I want is the result to be typed Result<string, never> or Ok<string> | Err<never>.

The problem is here: ReturnType<U> | ReturnType<F>. I want the return type of U and F given that U and F are typed relative to T and E.

How can I achieve something like ReturnType<U<T>> | ReturnType<F<E>>

Share Improve this question asked Nov 15, 2024 at 21:21 eakleakl 5711 gold badge10 silver badges24 bronze badges 4
  • TS cannot support arbitrary high order function types like this. The only support that exists is described at ms/TS#30215. The closest you can get to this exact code is this playground link but unfortunately the returned function can only accept any[], it cannot already have been generic. If you want that you have to give up on the currying thing and write it as in this playground link. Does that fully address the q? If so I'll write an a; if not, what's missing? – jcalz Commented Nov 15, 2024 at 22:07
  • Thanks @jcalz that’s helpful. It is possible to define the final return type explicitly while letting all other types to be inferred by TS? ˋcreateSafeFactory<R, U, F>(…) { … }ˋ and when invoking the function ˋcreateSafeFacrory<Ok<T>|Err<E>>(…)ˋ with T, the infered return type of onOk and E the infered return type of onErr – eakl Commented Nov 16, 2024 at 4:46
  • No, you cannot explicitly annotate them; the support in ms/TS#30215 does stuff that simply cannot be expressed in the type system. The types you'd want to annotate would involve unbound type parameters. Have I fully addressed the question now? Shall I write up an answer or am I missing something? (Note I asked this question before but you didn't answer it, could you please do so, so I can know how to proceed? Thank you.) – jcalz Commented Nov 16, 2024 at 15:53
  • Yes, you can, thanks for the explanation. – eakl Commented Nov 16, 2024 at 17:55
Add a comment  | 

1 Answer 1

Reset to default 0

You cannot perform arbitrary higher order generic function type manipulation. Generally speaking TypeScript will have to give up and erase generic type parameters or instantiate them with their constraints before manipulating them. So ReturnType<> acting on the type <T>(value: T) => Ok<T> will produce Ok<unknown> because the generic type parameter T inside the function ends up being instantiated with its implicit unknown constraint. TypeScript's type system is simply not expressive enough for this. In order to allow such things you might need higher kinded types as described in microsoft/TypeScript#1213, or instantiation types as requested in microsoft/TypeScript#50481.

There is some support for manipulating generic function types like this at the value level. That is, sometimes you can write generic functions that operate on generic functions and produce different generic functions, but you need actual functions to do this, and not just their types. This support was implemented in microsoft/TypeScript#30215, and it's quite restrictive in what it supports. In particular:

When an argument expression in a function call is of a generic function type, the type parameters of that function type are propagated onto the result type of the call if:

  • the called function is a generic function that returns a function type with a single call signature,
  • that single call signature doesn't itself introduce type parameters, and
  • in the left-to-right processing of the function call arguments, no inferences have been made for any of the type parameters referenced in the contextual type for the argument expression.

Generally speaking you cannot write things in terms of function types like O extends (arg: any) => any and E extends (arg: any) => any, but instead need to write things in terms of their argument and return types like OA, OR, EA, and ER. Unfortunately that isn't sufficient for your code to start working:

function createSafeFactory<OA, OR, EA, ER>(
    onOk: (arg: OA) => OR,
    onErr: (arg: EA) => ER
) {
    return <A extends any[]>(
        fn: (...args: A) => OA,
        errorFn?: (error: unknown) => EA
    ) => {
        return (...args: A): OR | ER => {
            try {
                const result = fn(...args)
                return onOk(result)
            } catch (e) {
                const error = errorFn ? errorFn(e) : e as EA //

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745670918a4639397.html

相关推荐

  • Windows下配置Golang开发环境,并安装配置GoLand IDE

    作者&#xff1a;非妃是公主 专栏&#xff1a;《Golang》 博客地址&#xff1a;https:blog.csdnmyf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁

    1小时前
    00
  • 开发体育直播系统后台权限设计实践分享|ThinkPHP 技术栈落地案例

    今天我们分享的是一套由 东莞梦幻网络科技 自研的体育直播源码,在 ThinkPHP + MySQL 技术栈的加持下,后台权限系统如何从0到1落地,并支撑整个平台稳定运行。一、整体架构设计代码语言:html复制用户端(APPH5P

    1小时前
    20
  • 如何使用python查询Prometheus监控数据

    一.环境准备软件库包版本python3.8prometheus-api-client0.5.7二.安装步骤代码语言:python代码运行次数:0运行复制pip3 install prometheus-api-client默认安装最新版本的p

    1小时前
    20
  • windows 配置 upx

    ​1、下载:2、解压:解压后​3、配置环境变量,右键我的电脑-——》属性——》高级属性:在Path中添加: D:ProgramFileupx-5.0.0-win644、验证cmd中输入:代码语言:txt复制upx --version

    1小时前
    20
  • MySQL 8.4 配置复制

    参考文档:.4enreplication-configuration.html1.先在源数据库主机的myf添加这几项代码语言:javascript代码运行次数:0运行复制[mysqld]server-id = 2binlog_forma

    1小时前
    00
  • MySQL8使用物理文件恢复MyISAM表测试

    我们现场测试一个场景,drop一张MyISAM表后,单独对这表进行物理恢复首先我们看一下secure_file_priv文件目录的位置代码语言:javascript代码运行次数:0运行复制mysql> show global vari

    1小时前
    00
  • Oracle linux 8 二进制安装 MySQL 8.4企业版

    使用命令ldd --version ldd 检查,确定MySQL 8二进制包版本代码语言:javascript代码运行次数:0运行复制[root@mysql8_3 ~]# ldd --version ldd安装libaio代码语言:java

    1小时前
    00
  • 电脑分区后进不了系统怎么办,修复教程

    电脑分区后进不了系统&#xff0c;可能的原因有多种&#xff0c;包括分区操作不当导致系统文件丢失或损坏、BIOS设置错误、引导文件未正确配置等。针对这些问题&#xff0c;可以尝试以下解决方法&#xff1

    1小时前
    00
  • UMIT:统一多模态多任务视觉

    随着深度学习的迅速发展,尤其是在医学影像分析领域的应用,越来越多的视觉-语言模型(VLMs)被广泛应用于解决复杂的健康和生物医学挑战。然而,现有研究主要集中在特定任务或单一模态上,这限制了它们在多种医学场景中的适用性和泛化能力。为了解决这

    58分钟前
    00
  • 2025年最受欢迎的10款免费CRM软件大对比

    在数字化转型浪潮下,越来越多的企业开始重视客户关系管理(CRM)系统。一个高效的CRM不仅能帮助企业理清客户脉络,还能提升销售效率、优化服务体验。2025年,市场上涌现了众多优秀的免费CRM软件,本文将为大家对比10款最受欢迎的产品,助您选

    57分钟前
    00
  • 1.8w字图解Java并发容器: CHM、ConcurrentLinkedQueue、7 种阻塞队列的使用场景和原理

    文章多图且内容硬核,建议大家收藏上一章《1.6w 字图解 Java 并发:多线程挑战、线程状态和通信、死锁;AQS、ReentrantLock、Condition 使用和原理》,我们开启了 Java 高并发系列的学习,透彻理解 Java 并

    48分钟前
    00
  • 初始JESD204B高速接口协议(JESD204B一)

    01、对比LVDS与JESD204JESD204B是逻辑器件和高速ADCDAC通信的一个串行接口协议,在此之前,ADCDAC与逻辑器件交互的接口大致分为如下几种。低速串行接口(I2C、SPI)、低速并行接口(包含时钟信号和并行数据信号,

    45分钟前
    00
  • HLS最全知识库

    HLS最全知识库副标题-FPGA高层次综合HLS(二)-Vitis HLS知识库高层次综合(High-level Synthesis)简称HLS,指的是将高层次语言描述的逻辑结构,自动转换成低抽象级语言描述的电路模型的过程。对于AMD Xi

    43分钟前
    00
  • 国产车载通信测试方案:车规级CAN SIC芯片测试技术解析

    随着智能网联汽车的快速发展,车辆内部电子控制单元(ECU)数量激增,动力总成、高级驾驶辅助系统(ADAS)、车身控制等功能对车载通信网络的稳定性与速率提出了更高要求。传统CAN FD总线在复杂拓扑中面临信号振铃、通信速率受限(实际速率通常低

    28分钟前
    00
  • OWASP TOP10

    什么是OWASP?它的全称是 Open Web Application Security Project(开放式 Web 应用程序 安全 项目)TOP 10OWASP Top 10的意思就是10项最严重的Web 应用程序安全风险列表 ,它总

    27分钟前
    00
  • Prometheus配置docker采集器

    Prometheus 配置 Docker 采集器Prometheus 是一个开源的监控系统和时间序列数据库,广泛用于容器化环境中。通过监控 Docker 容器,用户可以实时获取服务性能、资源使用情况等信息。本文将介绍如何为 Docker 容

    24分钟前
    00
  • Power BI 无公式实现帕累托图表

    帕累托分析(Pareto Analysis),也被称为8020法则、关键少数法则,是一种常用的管理工具,用于识别和处理影响业务的主要因素。看到李伟坚老师在Excel使用Vega实现了花式帕累托(参考:Excel 零公式实现高级帕累托图表)

    19分钟前
    00
  • Go 语言 Mock 实践

    Mock 是软件测试中的一项关键技术,尤其在单元测试领域,可谓是“顶梁柱”般的存在,几乎不可或缺。它通过模拟真实对象的行为,使我们能在不依赖外部系统的情况下,专注测试代码的核心逻辑。对于测试开发、自动化测试,乃至性能测试中的某些场景,合理使

    17分钟前
    00
  • 3、win10重装系统后Mysql环境和数据的恢复

    因为电脑是机哥的原因&#xff0c;重装了好几次电脑&#xff0c;因为我习惯把软件都装在D盘。所以很多东西都还比较好恢复&#xff0c;在网上学会了怎么不卸载重装数据库&#xff0c;自己记录以备后面自己查

    14分钟前
    00
  • 【Docker项目实战】使用Docker部署IT工具箱Team·IDE

    一、Team·IDE介绍1.1 Team·IDE简介Team IDE 是一款集成多种数据库(如 MySQL、Oracle、金仓、达梦、神通等)与分布式系统组件(如 Redis、Zookeeper、Kafka、Elasticsearch)管理

    3分钟前
    00

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信