0

When I try to access array that has type of either number[] | number with index I get error for subarray eventhough I check if element is of array type. To make things more confuse when I store reference to subarray in to variable it works without errors?

const minuteRange = [1, 2, 3, 4, [7, 9], [11, 13]];

const minRange = minuteRange;
// const lastEl: number[] | number =  minRange[minRange.length - 1] ?? 0;

// IF I REPLACE minRange[minRange.length - 1] ( last element in that array with lastEL up it works without error) 
const lastMinuteRangeNum = Array.isArray(minRange[minRange.length - 1])
    ? minRange[minRange.length - 1][1]
    : minRange[minRange.length - 1] ?? 0;

Typescript playground

8
  • Are you trying to use a tuple? Maybe this answer will give you some ideas. Otherwise, you have to narrow the expression before trying to index into it. Commented Nov 5, 2022 at 0:40
  • If you want the compiler to keep track of exactly which types are at which elements, you cannot annotate as a wide type like D, and you cannot just let the compiler infer the type using its default algorithm since you also get a too-wide type. You could instead use a const assertion to ask the compiler to prefer more specific types, like this playground link shows. Does that work for you or am I missing something? (Please mention @jcalz to notify me if you reply.) Commented Nov 5, 2022 at 1:00
  • Oh, wait, you do say "dynamic" a few times. So does that mean you don't want the compiler to keep track of the particular values? Then you have to do an actual test like this (which is what caTS said). How would you like to proceed here? Commented Nov 5, 2022 at 1:06
  • Actually this question is no good as I was trying to simplify question from this link: typescriptlang.org/play?#code/… Commented Nov 5, 2022 at 1:18
  • 1
    Okay, you've run into ms/TS#10530. The type of minRange.length - 1 is number, which is too wide for the compiler to perform control flow narrowing. See the answers to the linked questions. The workaround is, as you've already noticed, to save the problematic index result into a separate variable and check that. Commented Nov 5, 2022 at 2:02

1 Answer 1

0

If your const array is defined by yourself, I would advise to use the as const syntax which is going to automatically create a dedicated type matching exactly your const array :

const t = [1, 2, 3, [4, 5]] as const;
// no problem here, because typeof t is: readonly [1, 2, 3, readonly [4, 5]]
console.log(t[3][0]);

More on this on the literal inference section in the TS Reference

If your array is "dynamic" (created outside of your code), then I'd suggest to use Array.isArray() utility function, which is a type predicate and allows to seggregate the type of your array value at index 3 (given your type definition, it could either be a number or a [number,number])

Something like:

type D = Array<number | [number, number]>
const t: D = [1,2,3, [4,5]]; 

if(Array.isArray(t[3])) {
  // here, typeguard allows to consider that t[3] can't be of type number => by deduction, it will necessarily be of type [number,number]
  console.log(t[3][0]);
  // Note that t[3][1] will be ok too, and t[3][2] will produce an error, which is (I think) what you're expecting
}

More on Type Predicates in the TS Reference

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.