Typescript type for any non-array values

Viewed 126

Is there a way to make a specific type that matches anything except for arrays, for use with extends as a constraint on a generic value?

Things that don't work:

type NotArrayExplicit = Exclude<any, any[]>;
const shouldFail: NotArrayExplicit = [1,2,3];
// ^ Permitted by typechecker (opposite of desired behavior)

// This works for generics, yet can't be used with <T extends NotArrayGeneric>
type NotArrayGeneric<T> = T & Exclude<T, Array<any>>;
const rejectSuccess: NotArrayGeneric<number[]> = [1,2,3,4];
//                                                ^ Error: Type 'number' is not assignable to type 'never'.

function thisDoesntWork<T extends NotArrayGeneric<T>>(nonArray: T): T {return nonArray;}
//                                ^ Error: Type parameter 'T' has a circular constraint

Playground for above code.

The goal type is something that can be used as a constraint on a generic type, like <T extends NonArray>, in a clean and idiomatic way.

1 Answers

I don't know if you still need this, but you a workaround to make sure you're not getting a array value is putting the NotArrayGeneric type as the type of the param you want.

type NotArrayGeneric<T> = T & Exclude<T, Array<unknown>>;

function example<T>(nonArray: NotArrayGeneric<T>): T {
    return nonArray;
}

example(1) // this works
example([1]) // Type 'number' is not assignable to type 'never'

example({a: 1}) // this works
example([{a: 1}]) // Type 'number' is not assignable to type 'never'
Related