Is it possible to define a non empty array type in Typescript?

A feature request for allowing you to just check array.length > 0 to guard against empty arrays, microsoft/TypeScript#38000, was declined as being too complex. Essentially you cannot usually simply check length in TypeScript to convince the compiler about the availability of properties at given numeric keys.

You can define a non-empty array type like this:

type NonEmptyArray<T> = [T, ...T[]];

const okay: NonEmptyArray<number> = [1, 2];
const alsoOkay: NonEmptyArray<number> = [1];
const err: NonEmptyArray<number> = []; // error!

This is due to support added in TS 3.0 for rest elements in tuple types. I'm not sure what your use case is... It's probably more annoying to use that type than you expect, though:

function needNonEmpty(arr: NonEmptyArray<number>) {}
function needEmpty(arr: []) {}

declare const bar: number[];
needNonEmpty(bar); // error, as expected

if (bar.length > 0) {
    needNonEmpty(bar); // ugh, still error!
}

If you want a length check to work, you'll need to use something like a user-defined type guard function, but it's still annoying to use:

function isNonEmptyArray<T>(arr: T[]): arr is NonEmptyArray<T> {
    return arr.length > 0;
}

if (isNonEmptyArray(bar)) {
    needNonEmpty(bar); // okay
} else {
    needEmpty(bar); // error!! urgh, do you care?        
} 

Anyway hope that helps. Good luck!


Note for a happy reader. None of the above solutions is viable.

type NonEmptyArray<T> = T[] & { 0: T }
// type NonEmptyArray<T> = [T, ...T[]] -- same behavior

// ISSUE 1: map does not preserve Non-Emptiness
const ns: NonEmptyArray = [1]
const ns2 = ns.map(n => n) // number[] !!!

// ISSUE 2: length check does not make an array Non-Empty
function expectNonEmpty<T>(ts: NonEmptyArray<T>): any {}

if (ns2.length > 0) {
  expectNonEmpty(ns2) // type error
}

Just FYI, so you know why you'll unlikely to see NonEmptyArray in practice. The above replies should have mention this.


I've also wondered about this and came up with a different solution:

type NonEmptyArray<T> = T[] & { 0: T };

Tags:

Typescript