Open KiaraGrouwstra opened 6 years ago
@tycho01 I have found a way to iterate for unions
type A = 'a' | 'b' | 'c'
type D = {[a: A]: any}
declare var d: $ObjMap<D, () => string>
d.a
d.b
d.c
const s = d.unknown
// this is still string but access errors
/*
10: const s = d.unknown
^ string `unknown` [1] is incompatible with enum [2].
References:
10: const s = d.unknown
^ [1]
2: type D = {[a: A]: any}
^ [2]
*/
yeah $ObjMap
seems great :sweat_smile:, definitely seems one of the main features of Flow over TS (next to $Call
, $TupleMap
)
@tycho01 I thought conditional types is basically $Call
?
@goodmind: you're not far off! it has two use-cases:
map
on tuples / heterogeneous objects (both already covered in Flow utilities), as well as for e.g. function composition and currying.This is why I was kinda sad they gave us conditional types instead of $Call
, as it means the second use-case is likely still gonna remain pretty far off. And since those are pretty vital for FP libs like Ramda, that's why I'd feel more optimistic about Flow at this point.
@tycho01 hey! I implemented $Required
in Flow and it allows to implement Pick
in userland ($Rest doesn't remove optional properties because it is unsound in runtime, but we are at type-level :P)
https://github.com/facebook/flow/pull/7571
type RestFixed<T1, T2> = $Rest<T1, $Required<T2>>
type Pick<T1, T2> = RestFixed<
T1,
$ObjMap<RestFixed<T1, T2>, <V>(V) => any>
>;
declare var picked: Pick<{| a?: number, b?: string, c: string |}, {| a: any |}>;
picked.a; // void | number
picked.b; // error
picked.c; // error
@tycho01 with latest generic function inference in TypeScript it seems like Flow now far off from typing Ramda :(
that looks pretty cool! :D
with latest generic function inference in TypeScript it seems like Flow now far off from typing Ramda :(
Do you mean stuff like R.pipe(R.identity)
that got better in recent TS isn't as good yet in Flow?
Like, how do you see the major differences in expressiveness in TS and Flow now? Is it mostly about some of the TS additions like infer
?
edit: P.S. if you have a few types in a repo I can link it from the readme here, or alternatively I can give you push access over here if you want.
@tycho01 infer
can still be done with $Call
but new generic inference https://github.com/Microsoft/TypeScript/pull/30193, https://github.com/Microsoft/TypeScript/pull/30215 can't, but Flow now is more active in community than before
Also, I don't have typelevel stuff repository, because I'm not a fun of too much complex typelevel stuff after I've seen what styled-components
does in TS
💯
https://github.com/tycho01/typical/issues/27 also this lol
I often write simple flow code, and if something can't be typed I fork library
Yeah, type-level coding sucks, admittedly. In an ideal world, I hope we could just get down the hard stuff down once, after which end-users can just automagically enjoy the benefits without having to understand freaky type magic.
I'm shocked about the styled-components
example as well! Not because I haven't seen complex types before, obviously, but I find it surprising that a library at their level would need to make types this convoluted. I'd wonder if I'm missing something or if they really need it.
My idea had been perhaps most libraries could just use well-typed variables/functions instead of having to write their own custom type magic. Definitely curious as to what limitations forced them into this!
@tycho01 I was playing around OCaml and wrote this 🔥
Any examples I can implement with this?
hahaha, I'm quite confused about what happened in your example, but a type-level reduce is pretty cool! that's definitely among the remaining challenges in TS. I listed some potential use-cases for this based on Ramda here, though part may be solved already.
@tycho01 I just hardcoded reducing into object
| DefT (_, trust, ArrT arrtype), MapTypeT (reason_op, Reduce funt, tout) ->
let props = match arrtype with
| ArrayAT (_, opt) ->
(match opt with
| Some ts ->
Core_list.fold_left ts ~f:(fun acc value ->
match value with
| DefT (_, _, SingletonStrT str) ->
let t = EvalT (funt, TypeDestructorT (unknown_use, reason_op, CallType [value]), mk_id ()) in
SMap.add str (Field (None, t, Positive)) acc
| _ -> acc
) ~init:SMap.empty
| None -> SMap.empty)
| TupleAT (_, ts) ->
Core_list.fold_left ts ~f:(fun acc value ->
match value with
| DefT (_, _, SingletonStrT str) ->
let t = EvalT (funt, TypeDestructorT (unknown_use, reason_op, CallType [value]), mk_id ()) in
SMap.add str (Field (None, t, Positive)) acc
| _ -> acc
) ~init:SMap.empty
| ROArrayAT (_) -> SMap.empty in
let props_tmap = Context.make_property_map cx props in
let t =
let reason = replace_reason_const RObjectType reason_op in
let proto_t = ObjProtoT reason in
let call_t = None in
let dict_t = None in
let flags = {
exact = true;
sealed = Sealed;
frozen = false;
} in
DefT (reason, trust, ObjT {flags; dict_t; proto_t; props_tmap; call_t;})
(* Obj_type.mk_with_proto cx reason ~frozen:true ~sealed:true ~exact:true ~props proto *)
in
rec_flow_t cx trace (t, tout)
, perhaps this is more limiting to implement something like path
.
Not sure how would initial value behave if we can't subtract and add types?
// build object
var s: $Reduce<["a", "b"], <Acc, K>(Acc, K) => {
...Acc,
[K]: K // sorry no computed props in Flow
}, {||}> // failed example, should be special cased then
// build union without initial value
var s: $Reduce<["a", "b"], <Acc, K>(Acc, K) => Acc | K> // "a" | "b"
And this quickly becomes more complicated to implement
TypeScript in general allows more userland type magic, because all type utilities work well together but this isn't like this in Flow, $Values
breaks this $Reduce
example if you use switch
on resulting union
Working example for second reduce attempt, without initial value, because no idea how to do optional parameters
hm. I thought you showed me a path
Flow implementation before, using compose stuff to achieve the reduce-like functionality...
@tycho01, but Compose implementation doesn't work if you export function
really? I'm probably missing some context but that sounds kinda lame. :/
Yeah, all exports should be annotated properly for performance reasons, and you can't do this with compose(...spread)
. It would've worked if there was explicit $ComposeTuple
, but $Compose
is variadic
I managed to do initial value, but didn't found how to resolve generics for third argument :/
So it is pretty useless now
Aka flow would crash if you pass generic to third argument
ah, I see, thanks! if Flow has issues as well, then I guess we're probably boned for the time being. :joy:
There is also discord server with Flow maintainers, usually they help with contributing. I managed to add BigInt parsing and $Required
type only with their help
https://discordapp.com/invite/8ezwRUK
whoa, that's pretty nice! It's felt like the TS team has been fairly swamped with stuff, though at least recently Ramda got some attention there as it was the only typing repo convoluted enough to break on their recent changes. :joy:
Flow offers things like
$Call
which have been a bottleneck for me so far. I should finish up myflow
branch to get things up to parity. I'm not actually good at Flow so help welcome!edit: I should check
type-at-pos
to find the inferred types.