What do the ellipsis do in Flow type declarations? - flowtype

What are the ellipsis called in the following Flow code and what do they do?
export type ListTypeNode = {
+kind: 'ListType',
+loc?: Location,
+type: TypeNode,
...
};

This is the new syntax in Flow, that in the future will be indicating, that this object type is inexact (when the regular annotation by default will be exact object annotation).
In a few releases, Flow will begin to treat {foo: number} as an exact object. To indicate inexactness, you must add an ellipsis to the end of an object type: {foo: number, ...}. This new syntax forces the developers to opt into inexactness.
See more details here: https://medium.com/flow-type/on-the-roadmap-exact-objects-by-default-16b72933c5cf

Related

The operator '[]' isn't defined for the class 'Object'. Dart

I have a Widget which on some point navigate to a different page.
like:-
Navigator.of(context).pushNamed(
NextPage.routeName,
arguments: {
"tag": this.tag,
"data": this.data,
},
);
now clearly Even though argument parameter accepts type Object but it also accepts Map since this sentence is not giving me an error.
And in the NextPage i am accessing the value like:-
tag: ModalRoute.of(context).settings.arguments["tag"].toString(),
Now the vscode is giving me error:-
The operator '[]' isn't defined for the class 'Object'.
Try defining the operator '[]'.dart(undefined_operator)
I don't know why vscode is giving me an error.
So, Either the Object should have the [] or Map should be a type of Object.
Or there is something about the Date which is not clear.
Note: data is object.
How do I remove this error?
ModalRoute.settings.arguments is a property with the type Object. You cannot call an indexer [] on an Object. Everything in Dart inherits from Object, which is why you can pass your arguments to the ModalRoute no matter what it is. In order to use it, though, you need to first cast it to the type you are expecting to work with.
tag: (ModalRoute.of(context).settings.arguments as Map)["tag"].toString(),
ModalRoute.settings.arguments is a property with the type Object.so you can make it Map
final routeArgs = ModalRoute.of(context)!.settings.arguments as Map;
final tag = routeArgs['tag'];

How does Flow interpret generic types?

I would like to learn how Flow decides what type to use for a generic type, and if there is a way to control at what level the generic type gets inferred (what I mean by this is explained further down).
This question is inspired by How to type a generic function that returns subtypes. I think there is a distinction between the two questions because this one focuses on understanding how T is chosen, where as the linked on is focuses on typing the return type of a function.
The identity function is a great example to dissect. Its type is fairly straightforward
function identity<T>(value: T): T;
This seems like enough information to know what the implementation should be. However, I feel like this type is insufficient to know what the identity function actually does. For example, we could have (as the linked question tries to do),
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
Try Flow
This does not typecheck, with Flow complaining about returning the empty string. However, I would imagine in many languages that this would be fine--we are returning a string when a string was inputted, otherwise we are returning the original value of type T--but for some reason Flow does not like this.
My confusion is compounded by both this answer, where we can return value.substr(0, 0) instead of the empty string and Flow will no longer complain, and by the inability to return a strictly equal value,
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
Try Flow
I think a major reason for this discrepancy is that literals can act like types in Flow, in addition to the "JavaScript type". For example,
const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
are both valid. However, this means that when we have a function of type T => T, we do not know if Flow is inferring the literal or JavaScript type as the type.
I would like to know if there is some way of either knowing what Flow infers for generic types in a function or if there is a way to scope the generic type to be at the "literal" level or "JavaScript" level. With this ability, we could type function that coerces values to the default value for that type (i.e., strings would go to the empty string, numbers would go to 0). Here the type of the function would effectively be T => T, but hopefully Flow could be prevented from complaining about returning the default values.
Hoping to shed a little light here on what's going on, if not answer the question directly.
Let's take your first example first of all:
function identity<T>(value: T): T {
if (typeof value === 'string') {
return '';
}
return value;
}
The function signature is identity<T>(T): T. This is basically saying:
We are creating a new type T which could be anything (<T>).
Our function is going to receive a single argument of type T.
Our function is going to return a value of type T.
From this point forward, none of these restrictions are going to change, and the type of T is also not going to change. identity must return the exact type of T, not a subset of its type. Let's look at why.
identity<'some string'>('some string');
In this case the type of T is the literal type, 'some string'. In the case of this invocation of the above function, we would find that typeof value === 'string' and attempt to return '', a string. string, however, is a supertype of T which is 'some string', so we have violated the contract of the function.
This all seems rather contrived in the case of simple strings, but it's actually necessary, and much more obvious when scaling up to more complex types.
Let's look at a proper implementation of our weird identity function:
function identity<T>(value: T): T | string {
if (typeof value === 'string') {
return '';
}
return value;
}
A return type of T can only be satisfied by something which exactly matches T, which in the case of our signature can only be value. However, we have a special case where identity may return a string, so our return type should be a union of T | string (or, if we wanted to be super specific, T | '').
Now let's move on to this second example:
function identity<T>(value: T): T {
if (value === '') {
return '';
}
return value;
}
In this case, flow just doesn't support value === '' as a refinement mechanism. Refinement in flow is very picky, I like to think of it as a list of a few simple regular expressions that are run over my code. There's really only way to refine the type to a string, and that's by using typeof value === 'string'. Other comparisons won't refine to string. There's definitely also some wonkiness around refining generics, but something like this works fine (the refinement does, it still exhibits the previous generic-related error, of course):
function identity<T>(value: T): T {
if (typeof value === 'string' && (value: string) === '') {
return '';
}
return value;
}
(Try)
As for the substr example, that definitely looks like a bug to me. It seems you can do the same with any method on String that returns a string, such as concat or slice.
I would like to know if there is some way of either knowing what Flow infers for generic types in a function
Within the function body flow doesn't really infer the type of a generic. A generic has a concrete definition (T is T, essentially an unknown type, unless it has bounds, in which case it is an unknown type that matches those bounds). Flow may infer the types of parameters going into invocations of the function, but that should have no bearing on how the functions are written.
or if there is a way to scope the generic type to be at the "literal"
level or "JavaScript" level. With this ability, we could type function
that coerces values to the default value for that type (i.e., strings
would go to the empty string, numbers would go to 0). Here the type of
the function would effectively be T => T, but hopefully Flow could be
prevented from complaining about returning the default values.
The problem here is that this would no longer be T => T. As I've shown above, breaking such an implementation is trivial.

type parameter `U` of call of method `then`. Missing annotation

I have an object which might contain a promise property declared thus:
type PromiseAction = {
+type: string,
promise: ?Promise<any>,
};
The action argument to a function is declared to be of type PromiseAction:
(action: PromiseAction) =>
Later on I check whether the received action object does have a promise property and if action.promise has a then:
if (action.promise && typeof action.promise.then === 'function') {
If it does then I hook onto the promise chain:
return promise.then(
At which point I get the error: "type parameter U of call of method then. Missing annotation"
I can see in the source for flow that the then property of a Promise has a U parameter which, I assume, is the one being asked for.
How can an provide that U annotation if I only have only one parameter Promise<+R> in the type declaration?
You do not need to define the value of U.
The flow source you linked to means, essentially, "Promises returned by then fulfill with a value that is the same as either the return value of the handlers, or the fulfilled value of the returned Promise of those handlers." That sounds confusing (because Promises can be very confusing) but the bottom line is that it's not something you "fill out". It creates a relationship between the types that then returns and the types of the return values of onFulfill and onReject passed to then.
The error you're getting means that Flow can't figure out what that relationship is because it doesn't have enough information. Annotate the then callbacks with types:
return promise.then((a:string)=>...)
That will either fix the error, or at least disambiguate U enough to give you a more specific error.

How do I type a function with input and output objects with the same keys but different value types?

Basically, I have a function that will transform an object into a different object, and it's like a dictionary, but I don't know how to type it.
var myFunctions = {
a: () => something1,
b: () => something2,
[...]
}
gets transformed into
var myObject = {
a: something1,
b: something2
[...]
}
With Flow 0.33+ you can use $ObjMap
type ExtractCodomain = <V>(v: () => V) => V;
declare function f<O>(o: O): $ObjMap<O, ExtractCodomain>;
I don't think you can do this with Flow. The closest you can get is probably this:
function<T>(obj: T): ([key: $Keys<T>]: boolean)
That function is typed to return an object with the same key as input object, but with boolean-only values (as an example, you can specify another type). Sorry to disappoint, but it's hard to type highly dynamic code with Flow in general.
Note that the $Keys feature is undocumented because it's not part of the public API, so its behavior is defined solely by its implementation (in other words, it can change anytime).
If you're interested in the details of Flow's type system, check out the typings that come with flow in its own /lib directory, for example https://github.com/facebook/flow/blob/master/lib/core.js – you'll see that some things like Object.assign are special-cased, so you might not be able to re-implement such things in your own code.
Also, check out http://sitr.us/2015/05/31/advanced-features-in-flow.html for other "dollar features" such as $Shape and $Diff – it's partially outdated, but can give some good pointers.
#Nikita gave you the best answer for now. That said, the use-case you talked about is being discussed in the issues on the FlowType repository. It may land soon.
As of right now, if you've got mixed type, I'll just fallback to any
function<T>(obj: T): ([key: $Keys<T>]: any)
This way, at least the key names are validated. I expect within a few more versions of Flow, this problem will get solved.

what is #params in Iron:router

with meteor's IronRouter, I'm trying to use the this.params object elsewhere, but confused as to what it is. It seems to be a zero length array, that is actually an object with named methods after the path components.
# coffee
#route 'magnets',
path: '/magnets/lesson/:lessonCname'
data: ->
if #ready()
debugger;
console.log("route.params", #params)
with this code, in the debug console I will get:
this.params
[]
this.params.lessonCname
"despite-magnets-01"
typeof(this.params)
"object"
this.params.length
0
this.ready()
but in passing the params object to a server method, the methods (ie "lessonCname") disappear.
If my understanding is correct, then the near-term question is what is the best way to retrieve/convert these methods to {property:value} so they can be serialized and passed to server calls?
There are two easy ways of solving your problem, you can either set a global variable from within the data scope (but this is considered bad practice, at least IMO) or you can use the "data" function, which returns the data context for the current template:
data: ->
window._globalscopedata = #params.whatever #setting global variable
return someCollection.findOne #returns data context
_id: #params.whatever
when proccessing this route I will have the whatever param available in _globalscoredata and my document available in the template context.
Take a look at the source code for retrieving the parameters from a path. params is an array, but may have named properties. To iterate over everything, you can use the for in loop:
for(var x in myArray){
// Do something.
}
In this way, you can copy over everything to a new object (there may be a simpler way to create a copy).
The params property attached to a RouteController is an object with the following properties :
hash : the value of the URL hash.
query : an object consisting of key/value pairs representing the query string.
a list of URL fragments with their name and actual value.
Let's take an example, for this route definition :
// using iron:router#1.0.0-pre2 new route definition
Router.route("/posts/:slug");
And this URL typed in the browser address bar : /posts/first-post#comments?lang=en
We can use the console to find out precisely what params will actually contain :
> Router.current().params
Which will display this result :
Object {
hash: "comments",
slug: "first-post",
query: {
lang: "en"
}
}
Here slug is already a property of the params object whose value is "first-post", this is not a method.
If you want to extract from params these URL fragments as an object of key/value pairs, you can use underscore omit :
// getting rid of the hash and the query string
var parameters=_.omit(this.params,["hash","query"]);

Resources