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.
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.
I like first one because it mirrors TypeScript
There was a problem hiding this comment.
How can I make this support <>?
There was a problem hiding this comment.
@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
| 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.
This doesn't look right. Look at the FunT _, BindT _ rule for guidance.
- The
call_this_tfield of theBindTuse is the this type corresponding to the call of bind. E.g.,xinx.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.
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.
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.
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.
You mean something like this?
const appliedFn = Function.apply.bind(x); // ok
const reappliedFn = appliedFn.bind(y); // wrong
reappliedFn(null, [""]); // no errorThere was a problem hiding this comment.
@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 |
samwgoldman
left a comment
There was a problem hiding this comment.
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