type-fest icon indicating copy to clipboard operation
type-fest copied to clipboard

Add `ArrayAt` type for stricter `Array.prototype.at()` with tuples

Open jonahsnider opened this issue 2 years ago • 10 comments

See https://github.com/sindresorhus/ts-extras/issues/39 and https://github.com/microsoft/TypeScript/issues/47660.

const tuple = ['abc', 123, true] as const;

type First = ArrayAt<typeof tuple, 0>; // 'abc'
type Last = ArrayAt<typeof tuple, -1>; // true
type SecondToLast = ArrayAt<typeof tuple, -2>; // 123
type ThirdToLast = ArrayAt<typeof tuple, -3>; // 'abc'
type OutOfBounds = ArrayAt<typeof tuple, 999>; // should not compile since 999 does not extend 0 | 1 | 2 | -1 | -2 | -3
const array = ['abc', 123, true];

type First = ArrayAt<typeof tuple, 0>; // string | number | true
type Last = ArrayAt<typeof tuple, -1>; // string | number | true
type OutOfBounds = ArrayAt<typeof tuple, 999>; // string | number | true

jonahsnider avatar Mar 01 '22 04:03 jonahsnider

PR welcome

sindresorhus avatar Mar 01 '22 04:03 sindresorhus

Per discussion in https://github.com/sindresorhus/ts-extras/issues/39, should this be TupleAt?

sindresorhus avatar Mar 01 '22 06:03 sindresorhus

If we call this type TupleAt how would behavior with non-tuples work? Disallow non-tuple inputs? Rename but still support plain arrays?

jonahsnider avatar Mar 01 '22 06:03 jonahsnider

Why would you use it with non-tuples? It has no benefit.

sindresorhus avatar Mar 01 '22 06:03 sindresorhus

I'm thinking of cases where users might have a type param for an array or a tuple which is passed to this type. Might be good to gracefully handle those. Not sure if this would be an actual pain point though.

jonahsnider avatar Mar 01 '22 06:03 jonahsnider

Then I suggest we do two types:

  • TupleAt
  • ArrayAt

sindresorhus avatar Apr 01 '22 18:04 sindresorhus

What would the behavior of ArrayAt be? Would it be any different than typeof array[number]?

jonahsnider avatar Apr 01 '22 21:04 jonahsnider

What would the behavior of ArrayAt be? Would it be any different than typeof array[number]?

ArrayAt<Array, -1> should be the same as LastArrayElement<Array>

tychenjiajun avatar Apr 02 '22 08:04 tychenjiajun

For arrays I would expect string and boolean for First and Last respectively.

const array = ['abc', 123, true];

type First = ArrayAt<typeof array, 0>; // string
type Last = ArrayAt<typeof array, -1>; // boolean

skarab42 avatar Aug 31 '22 13:08 skarab42

Then I suggest we do two types:

  • TupleAt
  • ArrayAt

@sindresorhus But what if we have an array type T that sometimes is a tuple and sometimes not? If TupleAt and ArrayAt are segregated, then we won't be able to use any of them with T. I propose the following:

type T = number[] | [true, false];
type X = ArrayAt<T, -1>; // Should be `number | false`, IMO

papb avatar Sep 02 '22 15:09 papb