一系列连续的映射功能的通用打字稿类型

发布于 2025-02-10 20:12:22 字数 1794 浏览 2 评论 0原文

我正在通过一系列功能多次映射类型 a 的对象。
例如:

  1. 首先具有类型的函数(项目:a)=> b
  2. 然后具有类型的函数(项目:b)=> C
  3. 然后具有类型的函数(item:c)=> d

出现了类型d的对象。这些函数作为数组传递,在示例中将具有类型[(item:a)=> b,(项目:b)=> C,(项目:C)=> d]

一个具体的示例:

const mappings: MappingSequence<number, { duration: string }> = [
  a => a * 60,            // (a: number) => number
  b => `${b} seconds`,    // (b: number) => string
  c => ({ duration: c }), // (c: string) => { duration: string }
];

其中将所有3个函数应用于编号2导致{持续时间:'120秒'}导致。

我希望表达这种通用类型mappingSequence&lt; a,z&gt;

type Mapping<X, Y> = (item: X) => Y;

// This definition style won't work because there are infinitely many options
type MappingSequence<A, Z> =
  [] |                                            // 0-step mapping, only if A = Z
  [Mapping<A, Z>] |                               // 1-step mapping
  [Mapping<A, B>, Mapping<B, Z>] |                // 2-step mapping, for any B
  [Mapping<A, B>, Mapping<B, C>, Mapping<C, Z>] | // 3-step mapping, for any B and C
  // etc., for arbitrary array lengths

// This style might work, but the recursion is tricky
type MappingSequence<A, Z> =
  [Mapping<A, Z>] |
  [Mapping<A, B>, ...MappingSequence<B, Z>]; // for any B

但是,我已经陷入了多次不同的尝试。 关键困难似乎在表达“对于任何 b”。我想使用推断关键字来执行此操作;但是,然后我需要拥有有条件的类型,当拥有通用的az时,我发现这很难。

关于如何实现这一目标有任何见解吗?提前致谢。

I am mapping an object of type A multiple times via a succession of functions.

For example:

  1. first a function with type (item: A) => B
  2. then a function with type (item: B) => C
  3. then a function with type (item: C) => D

and out comes an object of type D. These functions are passed as an array, which in the example would have type [(item: A) => B, (item: B) => C, (item: C) => D].

An concrete example of this:

const mappings: MappingSequence<number, { duration: string }> = [
  a => a * 60,            // (a: number) => number
  b => `${b} seconds`,    // (b: number) => string
  c => ({ duration: c }), // (c: string) => { duration: string }
];

where applying all 3 functions to the number 2 results in { duration: '120 seconds' }.

I am looking to express this generic type MappingSequence<A, Z>:

type Mapping<X, Y> = (item: X) => Y;

// This definition style won't work because there are infinitely many options
type MappingSequence<A, Z> =
  [] |                                            // 0-step mapping, only if A = Z
  [Mapping<A, Z>] |                               // 1-step mapping
  [Mapping<A, B>, Mapping<B, Z>] |                // 2-step mapping, for any B
  [Mapping<A, B>, Mapping<B, C>, Mapping<C, Z>] | // 3-step mapping, for any B and C
  // etc., for arbitrary array lengths

// This style might work, but the recursion is tricky
type MappingSequence<A, Z> =
  [Mapping<A, Z>] |
  [Mapping<A, B>, ...MappingSequence<B, Z>]; // for any B

However, I've gotten stuck in multiple different attempts.
The key difficulty seems to be expressing "for any B". I was looking to do this with the infer keyword; however, then I need to have conditional types, which I found hard when having the generic over A and Z.

Any insights in how this can be achieved? Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

颜漓半夏 2025-02-17 20:12:22

使用通用功能时,这似乎可以实现:

type DecrementTable = [
  -1, 0, 1, 2, 3, 4, 5,
  6, 7, 8, 9, 10, 11, 12,
  13, 14, 15, 16, 17, 18,
  19, 20, 21, 22, 23, 24,
  25, 26, 27, 28, 29, 30,
  31, 32, 33, 34, 35, 36,
  37, 38, 39, 40, 41, 42,
  43, 44, 45, 46, 47, 48,
  49, 50
]

type Decrement<T extends number | string> = DecrementTable[T & keyof DecrementTable]

type MappingReturnType<T extends any[]> = T extends [...any, infer R] ? R : never

function mappingSequence<
  S, 
  F extends any[]
>(startValue: S, fn: [...{ 
    [K in keyof F]: (args: K extends "0" ? S : F[Decrement<K & string> & keyof F]) => F[K] 
  }]) : MappingReturnType<F> {
    
  return {} as any
}

让我们看看这是否有效。

const result = mappingSequence(23, [
  (a: number) => a * 60,            
  (b: number) => `${b} seconds`,    
  (c: string) => ({ duration: c }),
])
// const result: {
//     duration: string;
// }

如您所见,有一个小警告:您必须给每个函数参数一个明确的类型。 但是,如果给定类型不正确,TypeScript将出现错误。

const result = mappingSequence(23, [
  (a: number) => a * 60,            
  (b: string) => `${b} seconds`, // Error: '(b: string) => string' is not assignable to type '(args: number) => string'    
  (c: string) => ({ duration: c }),
])

游乐场

This seems to be possible to achieve when using a generic function:

type DecrementTable = [
  -1, 0, 1, 2, 3, 4, 5,
  6, 7, 8, 9, 10, 11, 12,
  13, 14, 15, 16, 17, 18,
  19, 20, 21, 22, 23, 24,
  25, 26, 27, 28, 29, 30,
  31, 32, 33, 34, 35, 36,
  37, 38, 39, 40, 41, 42,
  43, 44, 45, 46, 47, 48,
  49, 50
]

type Decrement<T extends number | string> = DecrementTable[T & keyof DecrementTable]

type MappingReturnType<T extends any[]> = T extends [...any, infer R] ? R : never

function mappingSequence<
  S, 
  F extends any[]
>(startValue: S, fn: [...{ 
    [K in keyof F]: (args: K extends "0" ? S : F[Decrement<K & string> & keyof F]) => F[K] 
  }]) : MappingReturnType<F> {
    
  return {} as any
}

Let's see if this works.

const result = mappingSequence(23, [
  (a: number) => a * 60,            
  (b: number) => `${b} seconds`,    
  (c: string) => ({ duration: c }),
])
// const result: {
//     duration: string;
// }

As you can see there is a small caveat: You have to give each function argument an explicit type. But TypeScript will give an error if the given type is incorrect.

const result = mappingSequence(23, [
  (a: number) => a * 60,            
  (b: string) => `${b} seconds`, // Error: '(b: string) => string' is not assignable to type '(args: number) => string'    
  (c: string) => ({ duration: c }),
])

Playground

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文