-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Fix Function.prototype.apply.bind #7879
base: main
Are you sure you want to change the base?
Conversation
src/typing/type_annotation.ml
Outdated
let reason = mk_reason RFunctionType loc in | ||
reconstruct_ast (FunProtoApplyT reason) None | ||
let reason = mk_reason RFunctionType loc in | ||
(match convert_type_params () with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is the only place we'd allow a sometimes-parameterized generic. the most similar concept is having a default, but in that case you'd have to use an empty <>
(https://flow.org/en/docs/types/generics/#toc-adding-defaults-to-parameterized-generics).
this feels like a new behavior that we should consider carefully. also, we should consider how it interacts with annotating this
, because it might be closer to Function$Prototype$Apply<this=T>
or whatever syntax we end up with for that -- we've gone back and forth about annotating this
for functions as either function f(this: T, ...)
or function f<this = T>(...)
(cc @gabelevi )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like first one because it mirrors TypeScript
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I make this support <>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mroch you can mimic second one with first one
type F<T> = (this: T) => void
A silly question: What significance does binding the apply function itself have? Can you share an example where the observable effect of checking is different? If it is significant, I think the solution of adding a this type to the internal representation of ApplyT is reasonable. However, I don't think we should add a type parameter to the annotation syntax, or at least not until the overall |
@samwgoldman it would be possible to apply |
Can you please share an example? :) |
|
https://github.com/lttb/flown/blob/master/src/Reduce/index.js type Apply<Fn, Args> = $Call<Function$Prototype$Apply<Fn>, null, Args>
// basically $Call with tuple
declare function reduce<Fn, A, T>(Fn, A, T): $Call<
Apply<
$ComposeReverse,
$TupleMap<T, Fn>,
>
, A>
export type _Reduce = typeof reduce
export type Reduce<Fn, A, T> = $Call<_Reduce, Fn, A, T> |
type MergeAll<T> = Reduce<<X, V>(V) => X => { ...$Exact<X>, ...$Exact<V> }, {}, T>
declare function mergeAll<A>(A): MergeAll<A>
const x = mergeAll([{ a: 1 }, { b: 1 }, { c: 1 }, { a: 2 }])
;(x: { a: 2, b: 1, c: 1 })
/**
* $ExpectError
*
* the type of 'c' is incompatible with number
*/
;(x: { a: 2, b: 1, c: '1' }) |
@goodmind I don't see how these examples are related to the linked issue, or Function.prototype.apply/bind. I would much prefer to focus on the problem at hand, since there's enough work involved in supporting the use case from Marshall's example.
|
src/typing/flow_js.ml
Outdated
@@ -5710,6 +5714,9 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = | |||
) call_args_tlist; | |||
rec_flow_t cx trace (l, call_tout) | |||
|
|||
| FunProtoApplyT (lreason, _), BindT (_, _, { call_this_t; call_tout; _ }, _) -> | |||
rec_flow_t cx trace (FunProtoApplyT (lreason, Some call_this_t), call_tout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right. Look at the FunT _, BindT _
rule for guidance.
- The
call_this_t
field of theBindT
use is the this type corresponding to the call of bind. E.g.,x
inx.bind(...)
. You want the first argument instead. - Looking at
FunT _, BindT _
as a guide, you'll notice some complexity missing from here. Specifically, the constraints involvingResolveSpreadsToMultiflowPartial
. This is needed to figure out the full argument list in the presence of spread arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What first argument? This is forwarding From
declare var regularApply: $Function$Prototype$Apply;
const b = regularApply.bind(fn) // would be `$Function$Prototype$Apply<typeof fn>`
to $Function$Prototype$Apply<typeof fn>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you write a test, I think you will see what I mean. You want the first argument passed to the bind call, which is not call_this_t
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, my mistake -- I was confusing BindT with FunProtoBindT. You're right that call_this_t
is the first parameter.
You do still need to do something with call_args_tlist
, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean something like this?
const appliedFn = Function.apply.bind(x); // ok
const reappliedFn = appliedFn.bind(y); // wrong
reappliedFn(null, [""]); // no error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samwgoldman I think you were right 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samwgoldman would this mean adding second argument with applied parameters? I think one argument is already too much
@samwgoldman now it should work, I didn't expose partial apply to arguments itself, only to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't think we should add a type argument to the type annotation.
@samwgoldman how it would work then for this case type Apply<Fn, Args> = $Call<Function$Prototype$Apply<Fn>, null, Args> |
We don't need it to fix #5681. If you want to support another case, I think that you should write up a more thorough proposal. For example, why should we support that? |
@samwgoldman maybe I can remove argument if you don't want it and decide later how to make my case work? (or write proposal) |
Yeah. I would want to have a full understanding of your use case before adding something. It's possible that another solution would be better. I'm not opposed to adding something later, but we need to follow a process to make sure that we're adding the right things for the right reasons. |
@samwgoldman removed it |
Probably related issue, methods doesn't have function prototype |
Fixes #5681
Also adds argument to
Function$Prototype$Apply