Mpx框架中利用TypeScript模板字符串类型实现链式Key类型推导

Mpx框架中利用TypeScript模板字符串类型实现链式Key类型推导

mpx Mpx,一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架 mpx 项目地址: https://gitcode.com/gh_mirrors/mp/mpx

引言

在现代前端开发中,类型系统的重要性日益凸显。TypeScript作为JavaScript的超集,为开发者提供了强大的类型支持。在滴滴开源的Mpx小程序框架中,我们面临一个具有挑战性的类型推导问题:如何为类似Vuex的数据仓库设计中的链式Key提供完善的类型推导?

本文将深入探讨如何利用TypeScript 4.1引入的模板字符串类型(Template Literal Types)特性,解决Mpx框架中链式Key的类型推导难题。

问题背景

在Mpx框架的数据仓库设计中,我们经常需要处理类似这样的场景:

createStoreWithThis({
  actions: {
    someAction() {
      let result = this.dispatch('deeperActions.anotherAction', 1)
      return result
    }
  },
  deps: {
    deeperActions: createStoreWithThis({
      actions: {
        anotherAction(payload: number) {
          return 'result' + payload
        }
      }
    })
  }
})

我们需要解决的问题是:

  1. 如何让dispatch('deeperActions.anotherAction', 1)获得正确的返回值类型推导
  2. 如何对dispatch的参数类型进行限制

TypeScript 4.1新特性解析

TypeScript 4.1引入了模板字符串类型,这一特性为我们的问题提供了解决方案。让我们先了解它的基本用法:

type Color = "red" | "blue"
type Quantity = "one" | "two"

type SeussFish = `${Quantity | Color} fish`
// 结果为:"one fish" | "two fish" | "red fish" | "blue fish"

这种类型拼接的能力正是我们解决链式Key类型推导的基础。

链式Key类型推导实现

基础工具类型

首先,我们需要定义几个基础工具类型:

// 过滤掉symbol类型的key
type StringKeyof<T> = Exclude<keyof T, symbol>

// 拼接两个key
type CombineStringKey<H extends string | number, L extends string | number> = 
  H extends '' ? `${L}` : `${H}.${L}`

核心推导类型

接下来,我们实现核心的链式Key推导类型:

type ChainKeys<T, P extends string | number = ''> = UnionToIntersection<{
  [K in StringKeyof<T>]: Record<CombineStringKey<P, K>, T[K]> & 
    (T[K] extends Record<any, any> ? ChainKeys<T[K], CombineStringKey<P, K>> : {})
}[StringKeyof<T>]>

这个类型递归地遍历对象的所有属性,生成所有可能的链式Key路径及其对应的类型。

类型转换工具

为了实现完整的推导,我们需要一个将联合类型转换为交叉类型的工具:

type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends 
  ((k: infer I) => void) ? I : never

这个工具利用了TypeScript的条件类型和逆变位置的类型推断特性。

完整实现

结合上述工具,我们可以实现完整的getValueByPath类型推导:

declare function getValueByPath<T extends Record<any, any>, K extends keyof ChainKeys<T>>(
  object: T, 
  prop: K
): ChainKeys<T>[K]

// 使用示例
let value = getValueByPath({ a: { b: { c: 1 } } }, 'a.b.c') // 类型为number

在Mpx数据仓库中的应用

将这一技术应用到Mpx框架的数据仓库中,我们可以实现完善的dispatch方法类型推导:

interface DeeperActions {
  [k: string]: ((...args: any[]) => any) | DeeperActions
}

type ActionChainKeys<T, P extends string | number = ''> = UnionToIntersection<{
  [K in StringKeyof<T>]: T[K] extends DeeperActions
    ? ActionChainKeys<T[K], CombineStringKey<P, K>>
    : Record<CombineStringKey<P, K>, T[K]>
}[StringKeyof<T>]>

type Promisify<T> = T extends Promise<any> ? T : Promise<T>

declare function dispatch<K extends keyof ActionChainKeys<Actions>>(
  key: K, 
  ...args: Parameters<ActionChainKeys<Actions>[K]>
): Promisify<ReturnType<ActionChainKeys<Actions>[K]>>

// 使用示例
let result = dispatch('deeperActions.anotherAction') // Promise<string>

性能优化与注意事项

在实际应用中,我们需要注意以下几点:

  1. 递归深度限制:TypeScript对递归深度有限制,过深的嵌套会导致类型推导失败
  2. 性能优化:可以通过减少不必要的递归和类型计算来优化性能
  3. 备选方案:对于特别复杂的场景,可以考虑使用反向解析的方案

总结

通过利用TypeScript 4.1的模板字符串类型,我们成功地为Mpx框架实现了链式Key的完善类型推导。这一技术不仅解决了数据仓库设计中的类型推导难题,也为其他类似场景提供了参考方案。

TypeScript的类型系统正在变得越来越强大,掌握这些高级特性可以帮助我们构建更健壮、更易维护的应用程序。在Mpx框架的持续演进中,我们将继续探索和应用这些先进的技术,为开发者提供更好的开发体验。

mpx Mpx,一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架 mpx 项目地址: https://gitcode.com/gh_mirrors/mp/mpx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舒禄淮Sheridan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值