TypeScript函数接受可变参数对

g6ll5ycj  于 2023-01-21  发布在  TypeScript
关注(0)|答案(1)|浏览(137)

有没有可能在TypeScript中输入一个函数签名,以便函数接受一个展开元组参数的平面列表?哇,这很难用语言来描述。例如:

type Pair = { type: string, value: number };

function makePairs(
  t1: string, v1: number,
  t2: string, v2: number,
  ...and_so_on: unknown
): Pair[] {
  // ...
}

makePairs('a', 1, 'b', 2, 'c', 3);
// => [
//   { type: 'a', value: 1 },
//   { type: 'b', value: 2 },
//   { type: 'c', value: 3 },
// ]

我对函数体的实现不感兴趣,只是想澄清一下--我只想把像[A, B]这样的元组转换成像[A, B, A, B, A, B, ...]这样的可变元组。
问题的第二步是让它在稍微复杂的Pair定义下工作,例如:

type StrPair = { type: 's', value: string };
type IntPair = { type: 'i', value: number };
type BoolPair = { type: 'b', value: boolean };
type Pair = StrPair | IntPair | BoolPair;

type PairType = Pair['type'];
// => 's' | 'i' | 'b'

type PairOfType<T extends PairType> =
  | (T extends 's' ? StrPair : never)
  | (T extends 'i' ? IntPair : never)
  | (T extends 'b' ? BoolPair : never);
// => PairOfType<'s'> === StrPair

type ValueOfType<T extends PairType> = PairOfType<T>['value'];
// => ValueOfType<'s'> === string

type PairTuple<T extends PairType> = [T, ValueOfType<T>];
// => ['i', 123] ✓
//    ['s', 'foo'] ✓
//    ['b', 'nope'] ⨉
// etc

// Now I'd like to be able to take something (conceptually) like this:
function makePairs(...t1: PairTuple, ...t2: PairTuple, ...moreTuples: unknown) {
  // ...
}

// And generalise to something like this:
function makePairs(...pairs: ...[...PairTuple[]]) {
  // ...
}

// ... but obviously neither of those syntaxes work

从本质上讲,我希望避免在调用函数时必须将每一对都 Package 在数组中,因为这似乎是不必要的开销(相信我,在使用这种方法的地方,开销 * 将 * 是显著的)。
有什么想法吗?

gg58donl

gg58donl1#

这不是最迷人的答案,也许还有更好的答案,但我相信这符合要求:

// This type will accept any tuple with a length of at least 2. It will then
// use the first two elements of the tuple to get the types for any remaining
// elements in the tuple, in an A, B, A, B pattern:
type VariadicTuple<T extends readonly unknown[]> =
    T extends [infer K, infer V, ...infer TT]
    ? TT extends readonly [unknown, ...unknown[]]
        ? [K, V, ...KnownVariadicTuple<TT, K, V>]
        : [K, V]
    : never[];

// This type is like VariadicTuple, but it forces the A, B, A, B pattern
// mentioned above
type KnownVariadicTuple<T extends readonly unknown[], Tk, Tv> =
    T extends [infer K, infer V, ...infer TT]
    ? K extends Tk ? V extends Tv ?
        TT extends readonly [unknown, ...unknown[]]
        ? [K, V, ...KnownVariadicTuple<TT, Tk, Tv>]
        : [K, V]
    : never[] : never[] : never[];

function makePairs<T extends readonly unknown[]>(...args:T extends VariadicTuple<T> ? T : never): void {
}

makePairs("", 1); // success
makePairs("", 1, "", 2, "", 3); // success

makePairs("", 1, "", 2, "");       // error: Argument of type 'string' is not assignable to parameter of type 'never'.(2345)
makePairs("", 1, "", 2, "", true); // error: Argument of type 'string' is not assignable to parameter of type 'never'.(2345)
makePairs("", 1, "", 2, 4, 3);     // error: Argument of type 'string' is not assignable to parameter of type 'never'.(2345)

不幸的是,never类型真的会抛出任何有用的错误消息。我无法找到解决方法,所以要么全有要么全无。要么以A, B, A, B模式传入参数,要么得到“never”错误,但无法快速找出是哪一个抛出了所有错误。

相关问题