Skip to content

A length>0 on a readonly array should guarantee destructured element is known, not undefinedΒ #42909

Closed
@cefn

Description

@cefn

Suggestion

πŸ” Search Terms

Array Destructuring, noUncheckedIndexedAccess, length check

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

A length check with length more than zero should eliminate undefined from possible values when destructuring.

Looking at the example below, it's impossible that destructuring [unshifted,...rest] from the array could lead to unshifted being undefined, since the length is checked first. Nevertheless when noUncheckedIndexedAccess is set in Tsconfig, it treats undefined as a possible path.

function unshiftOrNothing(items:number[]) : number{
    if(items.length > 0){
        const [unshifted, ...rest] = items;
        return unshifted;
    }
    return 0;
}

πŸ“ƒ Motivating Example

I have encountered this a lot when manipulating 'immutable' arrays e.g. for queues or redux state which require the use of destructuring, since array methods change the array.

I can't identify a type-safe alternative or check which ensures that the typescript compiler picks up on the fact the first item must be set.

πŸ’» Use Cases

A workaround is to lie to the compiler that items is a tuple containing at least one item. However, I don't know what knock-on effect that has on e.g. type guarantees about ...rest - it's presumably expected to be a zero-length tuple which might break other things.

function workaround(items:number[]) : number{
    if(items.length > 0){
        const [unshifted, ...rest] = items as [number];
        return unshifted;
    }
    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions