-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compiler fails to catch implicit any #34
Comments
getJson here indeed returns a This is expected behavior and not related to this library at all, but how the TS type system works. |
@pixeleet Is correct. Let's walk through the types to confirm, though. const handler = () =>
pipe(
// This is just a string
"https://api.github.com/users/denoland",
// pipe takes getJson `string => Task<any>` and returns `Task<any>`
getJson,
// Let's build out T.map first. The type for T.map is
// `<A, I>(fai: (a: A) => I) => (ta: Task<A>) => Task<I>`
// The render function (which we plug in for fai) has type `(user: User) => string`
// That means T.map(render) has type:
// `(ta: Task<User>) => Task<string>`
// But we pass it a `Task<any>`.
// We know that we can use an object of type `any` wherever we can use a `User` but
// we don't know if we can use `Task<any>` wherever we can use `Task<User>`
// If we can then that means that the `Task` type is *covariant*. Here, we say,
// a type *T* is covariant if for types *A* and *B*, with *A* being a subset of *B*, *T<A>* is a subset of *T<B>*.
// It turns out that functions are a little weird when it comes to covariance and contravariance.
// For function A to be a subtype of function B the arguments of function A must be contravariant
// to the arguments of function B *and* the return type of function A must be covariant to the
// return type of function B.
// Since we don't have any arguments for the `Task` type we only need the return type
// of `Task<any>` to be covariant to the return type of `Task<User>`. This means we
// only need to prove that `Promise<any>` can be used anywhere `Promise<User>` can.
// This should be trivial to see.
// Ultimately, from a type theory perspective, it is 100% ok to pass a `Task<any>` to the function
// `(ta: Task<User>) => Task<string>`.
T.map(render), // T.map has type
T.map((s) => new Response(s))
); Now, we know that the compiler isn't messing up, but the question of how to make this safe is still hanging around. I can think of a few options.
Hope this helps! |
Thank you for the elaborate answers. Sorry about the newbie question. With your answers I was able to find the source of my confusion. I'm used to working in a codebase that uses no-unsafe-return in it's eslint rules. Appears that Not sure how I'll end up resolving this, but big thank you for your help! |
Not sure if this is a bug or me doing something wrong.
Looking at the implementation of
Task.map
I would have thought the code below shouldn't pass the type checking.Any ideas welcome 😇
The text was updated successfully, but these errors were encountered: