4

I am astonished not to have an error in this code:

let myList: string[] = require('something')

let foo: string | undefined = myList[7]

foo.length // no error!

How can I make foo have type string | undefined?

I want the typescript compiler to raise Object is possibly 'undefined'.ts(2532) on the third line.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Bilow
  • 2,194
  • 1
  • 19
  • 34
  • 2
    `undefined` and `null` are two different things. Do you want `string | undefined` or `string | null`? And what does that have to do with `foo.includes('')`? The empty string `''` is neither `null` nor `undefined` – derpirscher Oct 14 '20 at 14:09
  • 1
    [Nullable](https://www.typescriptlang.org/docs/handbook/advanced-types.html#nullable-types) refers to both undefined and null – Bilow Oct 14 '20 at 14:12
  • 1
    I want tsc to say `Object is possibly 'undefined'.ts(2532)` on the third line – Bilow Oct 14 '20 at 14:13
  • Forget about the empty string, it was to demonstrate that `foo` is considered as string, instead of `string | undefined` – Bilow Oct 14 '20 at 14:15

2 Answers2

1

You can "cast" the result of the array access to (string|undefined)

let myList : string[] = require('something');
let v = myList[7] as (string | undefined); //v will have type string|undefined
let uv = v.length;  //will give an error that v is possibly undefined
derpirscher
  • 14,418
  • 3
  • 18
  • 35
1

It looks like TypeScript sees that myList is string[], so myList[7] is detected to be of type string.

Although it's assignable to something of type string | undefined, TypeScript determines that it can't be undefined, so it automatically narrows the type of foo to string only.

If you wanted this to produce an error, you would have to type myList as Array<string | undefined>:

let myList: Array<string | undefined> = require('something')

let foo: string | undefined = myList[7]
console.log(foo);
foo.includes('') // Error
foo.length // Error

If you want TypeScript to understand that accessing past the end of the array will result in undefined, use a tuple type instead:

let myList: [string, string] = require('something')
let foo = myList[7]

Then foo is typed as undefined, because it's past the length of the array.

If you don't know in advance how many elements the array has, the easiest way to get TypeScript to throw the error you want is really to type the array as Array<string | undefined>, since accessing any numeric index of the array may produce a string or undefined, despite the fact that the array's own-property values are all strings.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • While it would work, I do not want this because it makes no sense: myList will only contain strings, forever – Bilow Oct 14 '20 at 14:22
  • Only an out-of-bound access (`myList[n]` where `n >= myList.length`) would cause the element to be undefined – Bilow Oct 14 '20 at 14:23
  • If it will only contain strings, then why do you want `foo` to be typed as `string | undefined`? That'd be inaccurate. – CertainPerformance Oct 14 '20 at 14:23
  • Because of out-of-bound access, I do not know if 7 (or another number) is lesser than the array length – Bilow Oct 14 '20 at 14:25
  • @Bilow With that reasoning, every access to an array should be `sometype | undefined` That makes no sense. You are responsible for checking the bounds yourself. – derpirscher Oct 14 '20 at 14:25
  • Right, I want to check bounds, so I want to explicitly mark foo as nullable until I have performed the bounds check – Bilow Oct 14 '20 at 14:26
  • Saw your edit. This is sad. So really no proper way to force cast foo to a nullable type? – Bilow Oct 14 '20 at 14:29
  • @Bilow see my updated answer. Althoug IMHO one should check the array length before accessing a certain index. Most other languages will throw an error if you access an out of bound index. There may be reasons, this is handled differerent in JS – derpirscher Oct 14 '20 at 14:31
  • Thanks @CertainPerformance for the explanation on `so it automatically narrows the type of foo to string only.` – Bilow Oct 14 '20 at 14:35
  • 1
    Sounds like OP wants "[pedantic index signatures](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#no-unchecked-indexed-access)" as implemented in TS4.1. – jcalz Oct 14 '20 at 15:15