How to restrict function parameters types base on if the index of parameter is even or odd in typescript?

76 Views Asked by At

What I'm looking for is to have a function with infinite number of parameters and each parameter type is based on if its index is even or odd.

A contrived example:

flow(isMachineReady(), 'and', isWaterHot(), 'or', isMilkHot(), 'then', serveCoffee())

Odd arguments: () => boolean Even arguments: 'and' | 'or' | 'then'

So if the function was used like this I want to get error for the second argument type:

flow(isMachineReady(), **isWaterHot()**, 'or', isMilkHot(), 'then', serveCoffee())

Here is what I tried before but didn't work

type FlowArgCouple = [operand: () => boolean, operator: 'and' | 'or' | 'then']

function flow(...args: [...FlowArgCouple][]){
   ...
}
2

There are 2 best solutions below

0
hassan salehi On BEST ANSWER

Nice one Dimava, the solution you have works. Although for some reason it takes time for typescript to find out the error.

I find out another solution which also works well. But same issue, not always gives you the error as soon as it's there. But I add this answer as well, in case anyone else running to the same question can use it:

Playgournd

type Operand = () => boolean
type Operator = 'and' | 'or' | 'then'

type ValidRepeatedTuple<
    T extends readonly unknown[],
    U extends readonly unknown[]
> = U extends readonly []
    ? U
    : readonly [...T, ...(U extends readonly [...T, ...infer R] ? ValidRepeatedTuple<T, R> : [])]

type FlowArgs<U extends readonly unknown[]> = [
    ...ValidRepeatedTuple<[Operand, Operator], U>,
    Operand
]

function flow<U extends unknown[]>(...args: FlowArgs<U>){
}

flow(() => true)
flow(() => true, 'and', () => false)
flow(() => true, () => false)
flow(() => true, 'and')
flow(() => true, () => false, () => false)
flow(() => true, 'and', 'and')
0
Dimava On

Playground
But srsly consider some other structure

import { F } from "ts-toolbelt"

// [T1] or [T1, T2, T1] or [T1, T2, T1, T2, T1]
type ToOddEvenOdd<A extends any[], T1, T2, P extends any[]> =
    | A extends [] ? [...P, T1]
    : A extends [infer F extends T1] ? [...P, F]
    : A extends [infer F extends T1, infer S extends T2, ...infer L] ? ToOddEvenOdd<L, T1, T2, [...P, F, S]>
    : A extends [infer F, infer S, ...infer L] ? ToOddEvenOdd<L, T1, T2, [...P, T1, T2]>
    : [[A]]


type ODD = () => boolean
type EVEN = 'and' | 'or' | 'then'
function g<A extends any[]>(
    ...args:
        A extends ToOddEvenOdd<A, ODD, EVEN, []> ? A
        : 
        F.NoInfer< ToOddEvenOdd<A, ODD, EVEN, []> >
) { }

g(() => true)
g(() => true, 'and', () => false)
g(() => true, () => false)
g(() => true, 'and')
g(() => true, () => false, () => false)
g(() => true, 'and', 'and')