Flow equivalent to Java Wildcard? - flowtype

Is there an equivalent to Java's Wildcards in Flow?
Here's my example code I've been working on as a test:
type InterfaceType = {
var1 : number,
};
type ActualType = InterfaceType & {
var2 : string,
};
type InterfaceGenericType<T : InterfaceType> = {
var3 : T,
}
type ActualGenericType = InterfaceGenericType<ActualType> & {
}
class State<T : InterfaceGenericType<InterfaceType>> {
prop : T;
constructor(arg : T) : State<T> {
this.prop = arg;
return this;
}
}
let actual : ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric : ActualGenericType = {
var3 : actual,
}
let s2 = new State(actualGeneric);
This is the flow error I'm getting:
40: let s2 = new State(actualGeneric);
^ Cannot call `State` with `actualGeneric` bound to `arg` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in property `var3`.
References:
20: class State<T : InterfaceGenericType<InterfaceType>> {
^ [1]
7: type ActualType = InterfaceType & {
^ [2]
I know I can get around the issue by doing:
class State<I : InterfaceType, T : InterfaceGenericType<I>> {
but I'm trying to not have to declare both types.

We can trim down your code a bit to remove the class:
type InterfaceType = { var1: number };
type ActualType = InterfaceType & { var2: string, };
type InterfaceGenericType<T : InterfaceType> = {
var3: T,
};
let actual: ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric: InterfaceGenericType<ActualType> = {
var3: actual,
};
let v: InterfaceGenericType<InterfaceType> = actualGeneric;
I can't speak to Java since I don't know it, but I can tell you how to fix this. If we look at the error for this code:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ Cannot assign `actualGeneric` to `v` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in type argument `T` [3].
References:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ [1]
2: type ActualType = InterfaceType & { var2: string, };
^ [2]
4: type InterfaceGenericType<T : InterfaceType> = {
^ [3]
The core issue is that v's type InterfaceGenericType<InterfaceType> would for example allow you to do
v.var3 = { var1: 42 };
because that is a valid InterfaceType object. That isn't a valid ActualType object, but by assigning actualGeneric to v, you've essentially erased that type information, which means that if your code were allowed as-is, the assignment would corrupt your actualGeneric object's type.
The fix for this is to tell Flow that the var3 property is read-only, by changing
var3: T,
to be
+var3: T,

Related

Are { [string]: string } and { [string]: (string | number} } incompatible is right?

string and string | number are compatible, but { [string]: string } and { [string]: (string | number} } are incompatible.
Am i doing something wrong?
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgAqAjGALxgDOGATgJYB2A5gNya4GEBM5VtjTMAB8wDAK4BbAEZ4abbPiIBmXgG8wAbWr1mAXQBcfHYIC+8jkQAsazdoEGiPE+ihiGAYwx04DMBICGjAAUAJSqqGBgAG7+NNHEhiS8AORQcHDJbJExcVFciTwUUcRsEdGx0UqJKhTqUrGGyfU0yWBmZTnRlonWRUpszkA
/* #flow */
type T1 = string;
type T2 = string | number;
type T3 = { [string]: string };
type T4 = { [string]: T2 }
function main(){
var v1: T1 = 'foo';
var v2: T2 = v1;
var v3: T3 = { bar: 'bar' };
var v4: T4 = v3;
}
13: var v4: T4 = v3;
^ Cannot assign `v3` to `v4` because string [1] is incompatible with number [2] in the indexer property. [incompatible-type]
References:
5: type T3 = { [string]: string };
^ [1]
6: type T4 = { [string]: T2 }
^ [2]
Reason
This is normal and expected
If this was allowed, then you would be allowed to do
v4.foo = 1;
var expectAString : string = v3.foo;
And you would get a number even though due to v3's type you should have only got a string.
Solution
Mark the objects as read-only:
type T3 = $ReadOnly<{ [string]: string }>;
type T4 = $ReadOnly<{ [string]: T2 }>;
Flow try link
Since they are read-only this prevents you to actually do v4.foo = 1;.
Note
This is the same for array, see this answer.
Docs
$ReadOnlyArray’s type parameter is covariant while Array’s type parameter is invariant
Invariance, covariance

Is there a way to check nested option values in one pattern in F#?

Let's pretend we have the following types:
type Message {
text : Option<string>
}
type Update {
msg : Option<Message>
}
How do I match it in one line, like in C# using null-conditional operator i.e update?.msg?.text ?
Is there a way to do it like this?:
match msg, msg.text with
| Some msg, Some txt -> ...
| None -> ...
because I don't want to be writing 2 nested match expressions.
You have two Record types (missing the "=" in your example). To match some variable of Update type, you could do as follows:
type Message = { text : Option<string> }
type Update = { msg : Option<Message> }
let u = {msg = Some({text = Some "text"})}
//all 3 possible cases
match u with
| {msg = Some({text = Some t})} -> t
| {msg = Some({text = None})} -> ""
| {msg = None} -> ""

How to hide warning 30 using [#ocaml.warning "-30"]

I have two records depending on each other and they both have a field with the same name. How to use the ocaml.warning attribute to hide this warning? I'd like to avoid something like [###ocaml.warning "-30"] my types here [###ocaml.warning "+30"].
# type a = {a : int;} and b = { a:int;};;
Characters 30-36:
Warning 30: the label a is defined in both types a and b.
type a = { a : int; }
and b = { a : int; }
This might not be any better than the bracketing construct you want to avoid, but it works for me:
# module M = struct
type a = {a: int} and b = {a: int}
end [#warning "-30"];;
module M : sig type a = { a : int; } and b = { a : int; } end
# type a = M.a and b = M.b;;
type a = M.a
and b = M.b

Flow-js: How to create a variable consistent with an intersection of array and object

I have this valid flow-js definition for mysql :
declare type QueryResults = Array<Object> &{
insertId?: string | number,
affectedRows?: number,
changedRows?: number
};
And I've trying to create a variable that would be consistent with this definition. (You can try it here):
/* #flow */
type A = Array<Object>
type B = {
insertId?: string | number,
affectedRows?: number,
changedRows?: number
}
type C = A & B;
let a1: A = []
let a2: A = [{}]
let b1: B = {}
let b2: B = {insertId: 3}
let c: C
c = [] // Error, not complient with B
c.insertId = 5 // Error, not complient with A
Well, I just found out see here
c = {}.extend([])

Dictionary contains a certain value swift 3

I want to check if a string exists in any of the values in my Dictionary
Dictionary<String, AnyObject>
I know arrays has .contains so I would think a dictionary does too. Xcode tells me to use the following when I start typing contains
countDic.contains(where: { ((key: String, value: AnyObject)) -> Bool in
<#code#>
})
I just don't understand how to use this I know inside I need to return a Bool, but I don't understand where I put what String I'm looking for. Any help would be great.
contains(where:) checks if any element of the collection satisfies
the given predicate, so in your case it would be
let b = countDic.contains { (key, value) -> Bool in
value as? String == givenString
}
or, directly applied to the values view of the dictionary:
let b = countDic.values.contains { (value) -> Bool in
value as? String == givenString
}
In both cases it is necessary to (optionally) cast the AnyObject
to a String in order to compare it with the given string.
It would be slightly easier with a dictionary of type
Dictionary<String, String> because strings are Equatable,
and the contains(element:) method can be used:
let b = countDic.values.contains(givenString)
Since your values are AnyObject – Any in Swift 3 - you have to check if the value is a string. If yes check if the value contains the substring.
let countDic : [String:Any] = ["alpha" : 1, "beta" : "foo", "gamma" : "bar"]
countDic.contains { (key, value) -> Bool in
if let string = value as? String { return string.contains("oo") }
return false
}
However if you want to check if any of the values is equal to (rather than contains) a string you could use also the filter function and isEmpty
!countDic.filter { (key, value) -> Bool in
value as? String == "foo"
}.isEmpty
You may need to learn basic usage of contains(where:) for Dictionarys first:
For [String: Int]:
let myIntDict1: [String: Int] = [
"a" : 1,
"b" : 101,
"c" : 2
]
let myIntDict1ContainsIntGreaterThan100 = myIntDict1.contains {
key, value in //<- `value` is inferred as `Int`
value > 100 //<- true when value > 100, false otherwise
}
print(myIntDict1ContainsIntGreaterThan100) //->true
For [String: String]:
let myStringDict1: [String: String] = [
"a" : "abc",
"b" : "def",
"c" : "ghi"
]
let myStringDict1ContainsWordIncludingLowercaseE = myStringDict1.contains {
key, value in //<- `value` is inferred as `String`
value.contains("e") //<- true when value contains "e", false otherwise
}
print(myStringDict1ContainsWordIncludingLowercaseE) //->true
So, with [String: AnyObject]:
let myAnyObjectDict1: [String: AnyObject] = [
"a" : "abc" as NSString,
"b" : 101 as NSNumber,
"c" : "ghi" as NSString
]
let myAnyObjectDict1ContainsWordIncludingLowercaseE = myAnyObjectDict1.contains {
key, value in //<- `value` is inferred as `AnyObject`
//`AnyObject` may not have the `contains(_:)` method, so you need to check with `if-let-as?`
if let stringValue = value as? String {
return value.contains("e") //<- true when value is a String and contains "e"
} else {
return false //<- false otherwise
}
}
print(myAnyObjectDict1ContainsWordIncludingLowercaseE) //->false
So, in your case:
let countDic: [String: AnyObject] = [
"a" : 1 as NSNumber,
"b" : "no" as NSString,
"c" : 2 as NSNumber
]
let countDicContainsString = countDic.contains {
key, value in //<- `value` is inferred as `AnyObject`
value is String //<- true when value is a String, false otherwise
}
print(countDicContainsString) //->true

Resources