You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为解析器组合子库定义支持任意长度元组的类型安全map函数?

Solution for Generic mapN Parser Combinator with Arbitrary Tuple Length

Absolutely! TypeScript's advanced generics support lets us define this mapN function in a fully generic way that works with any length of parser tuple while preserving complete type safety. Here's how to implement it:

Generic Type Definition

First, let's update the MapN type to handle arbitrary-length parser tuples:

type Parser<T> = () => T;

// Generic mapN type that adapts to any parser tuple length
type MapN = {
  <T extends readonly Parser<any>[], Z>(
    parsers: T,
    fn: (...args: { [K in keyof T]: T[K] extends Parser<infer U> ? U : never }) => Z
  ): Parser<Z>;
};

How This Type Works

Let's break down the key parts:

  • T extends readonly Parser<any>[]: Constrains the input parsers to be a tuple (or array) of Parser instances. Using readonly ensures compatibility with both regular arrays and readonly tuple literals.
  • { [K in keyof T]: T[K] extends Parser<infer U> ? U : never }: This mapped type extracts the return type of each Parser in the tuple. For every index K in T, we infer the type U that the corresponding Parser returns, building a new tuple of those types.
  • (...args: ...) => Z: The function fn accepts arguments matching the extracted tuple of parser return types, and returns a value of type Z. The final result is a Parser<Z> that wraps this logic.

Test with Your Examples

Let's verify this works with your original test cases (including the previously commented-out ones):

// Dummy parser implementations for testing
const string: Parser<string> = () => "hello";
const number: Parser<number> = () => 42;

// Your existing implementation (with a small runtime escape hatch)
const mapN: MapN = (parsers, fn) => {
  return () => fn(...parsers.map(p => p()) as any);
};

// All these will have perfect type inference!
const p1 = mapN([string, number], (a, b) => [a, b] as const);
// Type: Parser<[string, number]>

const p2 = mapN([string, number, number], (a, b, c) => [a, b, c] as const);
// Type: Parser<[string, number, number]>

const p3 = mapN([string, number, string, string], (a, b, c, d) => [a, b, c, d] as const);
// Type: Parser<[string, number, string, string]>

const p4 = mapN([string, number, string, number, number], (a, b, c, d, e) => [a, b, c, d, e] as const);
// Type: Parser<[string, number, string, number, number]>

Quick Note on the Implementation

The as any in the runtime code is a tiny escape hatch—TypeScript can't fully verify that the parsed values match the tuple types at runtime, but the type definition guarantees compile-time safety for how you use mapN. If you want stricter runtime checks, you could add validation logic, but the type system already ensures your function fn receives the correct argument types.

内容的提问来源于stack exchange,提问作者Dogbert

火山引擎 最新活动