Custom type is incompatible with mixed - flowtype

The problem can be demo'd here
I want to define a function that can take an array of mixed types:
function foo(x: Array<mixed>): string {
// ... do something
}
Then I try to call it with an array off some custom object type:
type Thing = {
id: string
}
let array : Array<Thing> = [{id: 'hello'}];
foo(array);
... and I get the following error
Cannot call `foo` with `array` bound to `x` because `Thing` [1] is incompatible with mixed [2] in array element.`
Is there something that I'm not understanding about the mixed type. Why can't a use an array of objects as an argument?

It has to do with a mutability of the array argument. You can use $ReadOnlyArray to satisfy the requirement.
function foo(x: $ReadOnlyArray<mixed>): string {
// ... do something
return "hello";
}
type Thing = {
id: string
}
let array : Array<Thing> = [{id: 'hello'}];
foo(array);
Array's are passed by reference, not by value, so the value contained in the variable array can be modified within foo. e.g.
x.push(1)
array would no longer be an array of Thing only. So using $ReadOnlyArray means that x is immutable and therefore array is safe.

Related

Trouble with LINQ Returning Results Set from Object[] Array Within Object

Consider the following code:
var articlesDisplay = from product in db.ProductSearchData
select product.articles;
articlesDisplay = articlesDisplay.Where(a => a[].body.Contains(searchString));
I'm trying to load a results set, but get a compiler error using the array notation in the Where clause. How should I be going about this?
The desired end result is a var articlesDisplay object that can be used in ASP.NET MVC pagination.
Thanks to any/all for your assistance!
remove the array notation
var articlesDisplay = from product in db.ProductSearchData
select product.articles;
articlesDisplay = articlesDisplay.Where(a => a.body.Contains(searchString));
A lambda expression is just like a function declaration, but instead of method name(paramters){body } it takes the form of parameters => body. So this:
a => a[].body.Contains(searchString)
Is the same as this:
bool Method(Article article)
{
return article[].body.Contains(searchString);
}
That is obviously not valid, since it won't compile. You need a Func<T,bool>, or a function that accepts a single element and returns true or false depending on whether it is to be included. So you probably want this:
bool Method(Article article)
{
return article.body.Contains(searchString);
}
Which translates to this:
a => a.body.Contains(searchString).

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.

How to create a Flow Union runtime refinement without embedding literals

Hello kind Stackoverflow folks,
I'm trying to create a function to guard off code from being executed at run-time with an incorrect Flow type present.
My understanding is that the way to do this at run-time is by refining, or checking, that the type matches what is required and using Flow to keep an eye that no cases are missed along the way.
A simple case is where I have a string input that I would like to confirm matches to a enum/Union type. I have this working as I would expect with literals e.g.
/* #flow */
type typeFooOrBaa = "foo"| "baa"
const catchType = (toCheck: string): void => {
// Working check
if (toCheck === "foo" || toCheck === "baa") {
// No Flow errors
const checkedValue: typeFooOrBaa = toCheck
// ... do something with the checkedValue
}
};
Try it over here
Naturally, I would like to avoid embedding literals.
One of the things I've tried is the equivalent object key test, which doesn't work :-( e.g.
/* #flow */
type typeFooOrBaa = "foo"| "baa"
const fooOrBaaObj = {"foo": 1, "baa": 2}
const catchType = (toCheck: string): void => {
// Non working check
if (fooOrBaaObj[toCheck]) {
/*
The next assignment generates the following Flow error
Cannot assign `toCheck` to `checkedVariable` because: Either string [1] is incompatible
with string literal `foo` [2]. Or string [1] is incompatible with string literal `baa` [3].",
"type"
*/
const checkedVariable: typeFooOrBaa = toCheck
}
};
Try it over here
Is it possible to achieve something like this without having to go down the full flow-runtime route? If so how is it best done?
Thanks for your help.
One approach that appears to works is to use the const object which defines the allowed values, to:
Generate a union type using the $keys utility.
Use that union type to create a map object where the keys are the desired input (our case strings) and the values are "maybe"s of the type that needs refining.
Here's the example from earlier reworked so that it:
Sets the type up as we'd expect to allow either "foo" or "baa" but nothing else.
Detects when a string is suitably refined so that it only contains "foo" or "baa".
Detects when a string might contain something else other than what's expected.
Credit to #vkurchatkin for his answer that helped me crack this (finally).
/* #flow */
// Example of how to persuade Flow to detect safe adequately refined usage of a Union type
// at runtime and its unsafe, inadequately refined counterparts.
const fooOrBaaObj = {foo: 'foo', baa: 'baa'}
type typeFooOrBaa = $Keys<typeof fooOrBaaObj>
// NB: $Keys used inorder for the type definition to avoid aliasing typeFooOrBaa === string
// which allows things like below to correctly spot problems.
//const testFlowSpotsBadDefition: typeFooOrBaa = "make_flow_barf"
const fooOrBaaMap: { [key: string]: ?typeFooOrBaa } = fooOrBaaObj;
// NB: Use of the "?" maybe signifier in the definition a essential to inform Flow that indexing into
// the map "might" produce a "null". Without it the subsequent correct detection of unsafe
// unrefined variables fails.
const catchType = (toCheck: string): void => {
const myValue = fooOrBaaMap[toCheck];
if (myValue) {
// Detects refined safe usage
const checkedVariable: typeFooOrBaa = myValue
}
// Uncommenting the following line correctly causes Flow to flag the unsafe type. Must have the
// "?" in the map defininiton to get Flow to spot this.
//const testFlowSpotsUnrefinedUsage: typeFooOrBaa = myValue
}
Have a play with it over here
You can type the object as {[fooOrBaa]: number}, but flow will not enforce that all members of fooOrBaa exist in the object.

Construct deep struct is not working

i’ve struct which responsible to parse data from yaml file
While this struct is working sometimes I got some newly fields which I need to parse
This is working
- name: test1
type: type
path: path
This is not
- name: test1
type: type
path: path
build-parameters:
maven-opts:
defines:
skipTests: true
This is the struct
type Modules struct {
Name string
Type string
Path string
Parameters Parameters `yaml:"build-parameters,omitempty"`
}
And the parameters is type of:
type Parameters map[string]string
How I should construct my struct to accept this build-parameters entries also?
This is the library I use
https://github.com/go-yaml/yaml
Your struct does not match the data structure. If Parameters is a map[string]string, it accepts key-value pairs where the key and value are both string. In your data, build-parameters contains an object, which contains an object, which contains a key-value pair.
You could either redefine Parameters to be map[string]interface{} and use type assertions, or you can define the entire structure, e.g.:
type Parameters struct {
MavenOpts struct {
Defines map[string]string
} `yaml:"maven-opts"`
}
If you use the empty interface, you'll have to use type assertions, which can get pretty cumbersome. For example:
if opts, ok := module.Parameters["maven-opts"].(map[string]interface{}); ok {
if defines,ok := opts["defines"].(map[string]interface{}); ok {
if skipTests,ok := defines["skipTests"].(bool); ok {
// skipTests is the bool value from the yaml
}
}
}

How to add an array attribute to the collection?

I have global array of type string in my code that am trying to add to my collection.
i manage to add normal global string to the collection this way
previewImage: {
type: String,
autoValue: function() {
return PIurl;
}
}
is it possible to do it with a global array? if no, what alternatives i can use?
If you're just asking for defining a field that is an array of strings with SimpleSchema, you can do it like this:
myArrayOfStringsField: {
type: [String]
}
You can surround any of the valid SimpleSchema types in []'s to make them arrays of that type.

Resources