Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript length of a generic type

I have a function that looks like

export function is<T extends any[]>(...klasses): Predictor<T> {
    return (...variables: T) => {
        return variables
            .every((variable, index) => variable instanceof klasses[index]);
    }
}

In this example, I want to make sure klasses is of the same length as the array that T is. How do make klasses of type any[] with the same length as T?

Update

Based on the comments, I've updated this to

export function is<T extends Array<any>>(...klasses: any[] & {length: T['length']}): Predictor<T> {
    return (...variables: T) => {
        return variables
            .every((variable, index) => variable instanceof klasses[index]);
    }
}
like image 930
Kousha Avatar asked Oct 15 '22 15:10

Kousha


2 Answers

Here's another solution using a second type argument for the length. I saw that you don't want another type argument, but I'll add it for completeness sake anyway.

export function is<L extends number, T extends any[] & {length: L}>(...klasses: any[] & {length: L}) {
    return (...variables: T) => {
        return variables
            .every((variable, index) => variable instanceof klasses[index]);
    }
}

is(Number, String)(1, 1); // ok
is(Number, String)(1); // error
like image 138
Ingo Bürk Avatar answered Oct 20 '22 06:10

Ingo Bürk


You can query the length of the classes array, then restrict variables length to be the same:

export function is<TClasses extends any[]>(...klasses: TClasses) {
    return (...variables: any[] & { length: TClasses['length'] }) => {
        return variables
            .every((variable, index) => variable instanceof klasses[index]);
    }
}

is(Number, String)(1, 1); // ok
is(Number, String)(1); // error: Types of property 'length' are incompatible

But in this case you won't be able to specify variables type upfront. If you can change implementation to allow automatic inference of classes type, you can go with:

export function is<T extends any[]>() {
    return <TClasses extends any[]>(...klasses: TClasses) =>
        (...variables: T & { length: TClasses['length'] }) =>
            variables.every((variable, index) => variable instanceof klasses[index]);
}

is<string[]>()(Number, String)('1', '1'); // ok
is<string[]>()(Number, String)(1, 1); // error: not assignable to type 'string[]'
is()(Number, String)(1); // error: Types of property 'length' are incompatible
like image 29
Aleksey L. Avatar answered Oct 20 '22 06:10

Aleksey L.