Kousha
Kousha

Reputation: 36199

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]);
    }
}

Upvotes: 6

Views: 4583

Answers (2)

Aleksey L.
Aleksey L.

Reputation: 37918

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

Upvotes: 1

Ingo B&#252;rk
Ingo B&#252;rk

Reputation: 20033

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

Upvotes: 2

Related Questions