#I upgraded typescript and now I don't know how to pass a variable into `Array.includes` anymore.

56 messages · Page 1 of 1 (latest)

cursive ruin
#

How do I make this code compile? The reason I'm using includes is because I'm trying to check if this is in the array or not. I don't think it's something I can prove at compile time when it comes from user input.

robust quailBOT
#
sleepeasysoftware#0

Preview:```ts
const externalVariable = "d"
const x = ["a", "b", "c"] as const

console.log(x.includes(externalVariable))```

cursive ruin
#

!ts

robust quailBOT
#
const externalVariable = 'd';
const x = ['a', 'b', 'c'] as const;

console.log(x.includes(externalVariable));
//                     ^^^^^^^^^^^^^^^^
// Argument of type '"d"' is not assignable to parameter of type '"a" | "b" | "c"'.```
cursive ruin
#

I figured it out

robust quailBOT
#
sleepeasysoftware#0

Preview:```ts
const externalVariable = "d"
const x = ["a", "b", "c"] as const

console.log(
x.includes(externalVariable as typeof x[number])
)```

cursive ruin
#

Is there a better way?

young trail
#

!*:includes

robust quailBOT
#
retsam19#0
`!retsam19:includes`:

The type of Array.prototype.includes assumes that you're using the string to check something about the array. If you're trying to do the reverse, a modified type signature is useful:

function includes<S extends string>(haystack: readonly S[], needle: string): needle is S {
    const _haystack: readonly string[] = haystack;
    return _haystack.includes(needle)
}

declare const x: string;
if(includes(["a", "b", "c"], x)) {
    // x is "a" | "b" | "c"
}
cursive ruin
#

What does "if you're trying to do the reverse" mean? If I'm trying to use an array to check something about the string?

toxic tartan
cursive ruin
#

Isn't it arr.includes(potentialElement) in both cases?

#

!helper I'm trying to understand the part of !*:includes that says, "The type of Array.prototype.includes assumes that you're using the string to check something about the array. If you're trying to do the reverse..." How am I trying to do the reverse?

vapid narwhal
#

Basically, ["a", "b", "c"].includes("d") is meaningless because it should return true and if it doesn't then your type system will be broken. It could be made that it allows an unknown value but it will f*ck up the type system.

#

If you have a more specific example that's broken too, please share

cursive ruin
vapid narwhal
cursive ruin
#

at that location, the "d" comes from user input

#

but elsewhere, as const's relied on for the type safety

#

(I can be more specific if I'm not making sense)

wet patrol
#

It would be better to include more of your code instead of the contrived example.

vapid narwhal
cursive ruin
silk stag
#

That's what the includes signature is meant for.

#

The other case, the case that Array.prototype.include is really typed for looks like:

type Item = "donut" | "muffin" | "coffee";

function checkout(cart: Item[]) {
    if(cart.includes("donut") && cart.includes("coffee")) {
        applySpecialDiscount();
    }
}

in this case the item is known and the array is the unknown.

#

And the TS types are written to prevent mistakes like cart.includes("covfefe"): that's not a valid item to have in the cart, so it's an error.

But if you were doing ["coffee", "donut", "muffin"].includes("covfefe") as a form of input validation, it would make sense... but the types can't distinguish between these two cases

#

It's not that the code is really different: in both cases it's really the same includes check, but the two signatures imply a different intent.

cursive ruin
#

🤔 okay I get it in those terms. Thank you

cursive ruin
# robust quail

Is there something wrong with this wording:

The type of Array.prototype.includes assumes that the array elements are unknown at compile time. If they are known, a modified type signature is useful:

#

@silk stag ^

silk stag
#

Seems pretty equivalent, yeah.

toxic tartan
#

ah, im 🐌 lol

cursive ruin
#

See, I thought the reverse string/array sentence was about the content at runtime. I didn't realize was about the type system

silk stag
#

Not sure it's significantly better; I kinda like the phrasing in terms of "intent"; but if it continues to be a point of confusion I'll keep it in mind.

toxic tartan
#

i think the original is broader, handling more cases

type Option = "a" | "b" | "c";
const option1 = ["a", "b"] as const satisfies Option[];
const option2 = ["a", "c"] as const satisfies Option[];
function x(options: Option[]) {
  // check options
}

x(option1);
```in this case, a beginner might reason that the arrays are known at compiletime (even though `x` specifically doesn't know)
cursive ruin
#

!ts

#

oops

#

!playground

robust quailBOT
toxic tartan
#

well, TIL that's a thing.

cursive ruin
toxic tartan
#

i think it'd be hard to put scoping/separation of concerns into consideration in a single sentence

#

i'd probably just expand "if you're trying to do the reverse" into its own sentence, personally

#

ie, "if you're using a known array to check the string"

cursive ruin
#

The way I think about it, "reverse" doesn't come into play. I understand what it means now, but I don't understand the lesson I'm supposed to take away

#

The way I think about it is, "if the compiler knows every possible element, includes won't compile if you pass an element that can't be in the array" or something

#

Maybe I'm missing something really important about the "reverse" part

toxic tartan
silk stag
#

I don't think it's so much that one wording is significantly better than the other (though again, I like the framing it in terms of intenet); I think you just had to look at it from a few different angles until it clicked.

#

I probably should make a second snippet with some example code like the cart example which I've used a few times.