#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)
Preview:```ts
const externalVariable = "d"
const x = ["a", "b", "c"] as const
console.log(x.includes(externalVariable))```
!ts
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"'.```
I figured it out
Preview:```ts
const externalVariable = "d"
const x = ["a", "b", "c"] as const
console.log(
x.includes(externalVariable as typeof x[number])
)```
Is there a better way?
!*: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"
}
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?
yes, that's exactly what you're doing
I keep trying to comprehend that but come up short. Would it be possible to show both versions side by side?
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?
No, you're good. Includes can only check if a value possibly in an array is in it because it shouldn't change the type of the array otherwise. If you remove "as const" then it will work because your array would be able to contain "d" but now, you know by the type definition that it shouldn't.
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
But I'm using as const elsewhere for that same benefit
Then what's the point of the includes ?
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)
It would be better to include more of your code instead of the contrived example.
Okay, then it makes sense. Typescript could use a better typing on that function but it would have other effects so better to just use the good old "as" even tho it's better not to.
okay but if I do that that'll be a lot of irrelevant files and more to read
@cursive ruin You're "doing the reverse" because you're using the array to check something about the string: you have a known array a ["a", "b", "c"] and an unknown string externalVariable - you're using the array to narrow the string.
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.
🤔 okay I get it in those terms. Thank you
Is there something wrong with this wording:
The type of
Array.prototype.includesassumes that the array elements are unknown at compile time. If they are known, a modified type signature is useful:
@silk stag ^
Seems pretty equivalent, yeah.
right, for js. but the intent is different (string known, check array vs array known, check string), and the built-in version only accounts for the former
ah, im 🐌 lol
Would you be against using wording similar to my version?
See, I thought the reverse string/array sentence was about the content at runtime. I didn't realize was about the type system
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.
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)
well, TIL that's a thing.
hm, I consider myself closer to beginner than expert; could that be addressed by a minor edit to my version?
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"
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
well it's a bit more complex than that, since in most of these cases the unknown element is just a broader type than the array's elements
the compiler doesn't know if it can be in the array. which i guess is kinda hard to reason about out of context of the array interface as a whole
Honestly, kinda feeling this: https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/
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.