Hacker News new | ask | show | jobs
by redmorphium 1207 days ago
Interesting. Do these represent oversights in TypeScript's builtin type definitions, or are they artifacts of legacy considerations?
1 comments

I think TypeScript's decisions about these methods were done either because early versions of the transpiler couldn't detect certain behaviour or to make the language friendlier to work with.

If you have a function `widgetifyFoo(object: Foo)` and some JSON that you're pretty sure returns a `Foo`, you'd like to be able to do `widgetifyFoo(JSON.parse(data))`. With `any` the method call should just work, but with `unknown` you need to be explicit in what kind of data you're expecting (`widgetifyFoo(JSON.parse(data) as Foo)`). That may be too verbose for some.

The `[1, 2, undefined].filter(x => !!x)` typing is just a limitation of the transpiler. The end result will be an array containing two numbers, but you need to do some complicated validation to get the type right; `filter`s can be very complex if you want them to be, and they do by contract return a (sub)selection of the input. The type guarantee you want as a developer is based on the implementation rather than the underlying type system.

I suspect the `Array.includes` implementation to be a choice by the devs. The check will only succeed if the type matches, so you have a choice between setting the right type on your array (`"matt"|"sofia"|"waqas"|"bryan"` if you want to check for `"bryan"`) or using the type error you receive as an indication your check makes no sense. If you have an immutable array of specifically `["matt","sofia"]`, why even check for `"bryan"`? It won't be there, unless you make a mistake!

Javascript will obviously happily call the method for you and do what you expect, so TypeScript is breaking valid JavaScript standard API code here. That said, I agree with the TypeScript devs that they made the right choice on that one.

> I suspect the `Array.includes` implementation to be a choice by the devs. The check will only succeed if the type matches, so you have a choice between setting the right type on your array (`"matt"|"sofia"|"waqas"|"bryan"` if you want to check for `"bryan"`) or using the type error you receive as an indication your check makes no sense. If you have an immutable array of specifically `["matt","sofia"]`, why even check for `"bryan"`? It won't be there, unless you make a mistake!

That logic only applies to searching for a constant, and in that case it should go a step further and complain that you're using .includes at all. There's no reason to search for the constant "bryan" or the constant "matt".

The only time it makes sense to use .includes on this array is with a string of [semi-]unknown contents. And yet that's what gets blocked. The typing is wrong.

But TypeScript does have typed strings. If the array contains arbitrary strings, then the includes() call would just succeed without error.
If you typed the array as string[], it would generally work, but then the compiler wouldn't be able to warn you that users.includes("matt") is always true. And it wouldn't be able to warn you that users.includes("bryan") is always false.

I could at least understand the use case for a narrow includes if you explicitly typed the array as `("matt"|"sofia"|"waqas")[]`. But that's not the type of the array. The type is `readonly ["matt", "sofia", "waqas"]`. There's always exactly one of each. There is never a reason to feed a string of type `"matt"|"sofia"|"waqas"` into includes. The only sensible parameters for includes are types that intersect with `"matt"|"sofia"|"waqas"` but can be other things too. Which means the most sensible parameter type to derive is `string`.