question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

.includes or .indexOf does not narrow the type

See original GitHub issue

TypeScript Version: 3.7.x-dev.201xxxxx

Search Terms: .includes type narrowing, .indexOf type narrowing

Code

interface TextMessage {
    type: 'text',
    text: string
}

interface ImageMessage {
    type: 'image',
    url: string
}

type Message = TextMessage | ImageMessage;

// This is an example to reproduce the error in Playground.
// In practice, assume this message comes from outside our control (e.g. HTTP request)
const message: Message = JSON.parse(prompt('') as string) as Message;

if (message.type === 'text') {
    // No error here
    message.text = message.text.trim();
}

// Same for ['text'].includes(message.type)
if (['text'].indexOf(message.type) > -1) {
    // Error: Property 'text' does not exist on type 'ImageMessage'
    message.text = message.text.trim();
}

Expected behavior: I expect message to narrow its type to TextMessage inside if (['text'].indexOf(message.type) > -1).

Same way it does inside if (message.type === 'text')

Actual behavior: message is typed as TextMessage | ImageMessage inside the if block

if (['text'].indexOf(message.type) > -1) {
    // Error: Property 'text' does not exist on type 'ImageMessage'
    message.text = message.text.trim();
}

Playground Link: Provided

Related Issues: https://github.com/microsoft/TypeScript/issues/9842

My argument is that if (message.type === 'text') should be considered equivalent to if (['text'].includes(message.type)).

It might seem irrelevant on a small example like this, but if the array (['text']) is large, the workaround is difficult to maintain.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:25
  • Comments:16 (8 by maintainers)

github_iconTop GitHub Comments

19reactions
iansan5653commented, Jun 12, 2020

If #36352 were revisited, the includes function could be a lot more useful if the return type were an assertion, like:

interface Array<T> {
-    includes(searchElement: T, fromIndex?: number): boolean;
+    includes(searchElement: any, fromIndex?: number): searchElement is T;
}

Then, this would be fine:

if(["text"].includes(message.type)) {
  message.type; // "text"
}

IMO this is a much better way to type it than the current restriction, considering that the way it’s currently typed makes includes pretty much useless with arrays of enumerated types.

indexOf is a bit trickier and I wouldn’t necessarily support changing that definition.

6reactions
RyanCavanaughcommented, Nov 20, 2020

@JGJP Why doesn’t TypeScript already have every feature it will eventually need? The answer is that we haven’t designed or completed those features yet, which is why we have an issue tracker and are employing a large team of developers to work on it. Having humans work on the product at a finite speed is in fact the best we can do, for now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

TypeScript: Array.includes on narrow types - fettblog.eu
The Array.prototype.includes function allows searching for a value within an array. If this value is present, the function returns true!
Read more >
Array indexOf() vs includes() perfomance depending on ...
I made a test using array with 10 000 numeric values, here is results: Chrome: beginning. includes (22,043,904 ops/sec); indexOf ...
Read more >
Array.prototype.indexOf() - JavaScript - MDN Web Docs
The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is...
Read more >
ArrayList (Java Platform SE 7 ) - Oracle Help Center
Class ArrayList<E> ; int, indexOf(Object o). Returns the index of the first occurrence of the specified element in this list, or -1 if...
Read more >
Array .indexOf() method not working - ServiceNow Community
Solved: I realize this could be a general JavaScript question, but it seems like it should be working in the "real world" (and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found