Search code examples
typescriptlodash

Why is lodash _.sample function returning type number[] | undefined


Example 1

const myNumber = _.sample([1, 2, 3]);
// Expected type: number
// Actual type: number

Example 2

const arr = [1, 2, 3]
const myNumber = _.sample(arr);
// Expected type: number
// Actual type: number | undefined

Why is Typescript giving the type number | undefined in the second case, but not the first case?


Solution

  • This is happening because in one case the length of the array is known, and in the other it is not. If the length might be 0, then undefined is a possible output.

    When you do const arr = [1, 2, 3], the type on arr is number[]. That means it's an array of numbers which can grow and shrink. You and I can see that it's not going to shrink to 0-length before the next line, but the type information doesn't include that knowledge. So when it gets passed into sample, the types say that undefined is a possible result.

    When you create and use the array in the exact same spot, as in _.sample([1, 2, 3]);, typescript is able to assume a stricter type because it knows how it is being used. The type is inferred to be [number, number, number], ie a tuple of length 3, and therefore an element will definitely be returned.

    If you want to make this happen with the array being on a separate line, you'll need to tell typescript you want a stricter type on arr. The simplest way to do this is with as const, which tells typescript you won't be changing this array:

    const arr = [1, 2, 3] as const; // readonly [number, number, number]
    const myNumber = _.sample(arr); // number
    

    You can see the type definitions for sample here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/lodash/common/collection.d.ts#L1641 . The tuple case is handled by this type:

    sample<T>(collection: readonly [T, ...T[]]): T;
    

    And the array case is handled by this type:

    sample<T>(collection: Dictionary<T> | NumericDictionary<T> | null | undefined): T | undefined;