Why can't I use an Intersection type here but spreading one type into another works? - flowtype

Today I found out that I can do the latter here, but not the former, and I can't figure out why.
This does NOT work:
type User = {| id: number |}
type ExternalUser = User & {| externalUser: boolean |}
checkUser = (user: User | ExternalUser) {
if (user.externalUser) {
} else {
}
}
But this does:
type User = {| id: number |}
type ExternalUser = {| ...User, externalUser: boolean |}
checkUser = (user: User | ExternalUser) {
if (user.externalUser) {
} else {
}
}
In the first example, flow tells me externalUser does not exist in User on the if statement check. What am I missing? Seems like the end result should be the same. Possible Flow bug or am I breaking a rule that I'm not aware of?

Related

how to use react-hook-form setValue in a generic function?

This is my original function, which I would like to transform to a generic one:
import { FieldValues, useForm, UseFormSetValue } from 'react-hook-form'
type FormData = {
lid: number
lang_id: number,
source_id?: number | string | null
town_module_id?: number | null
region_module_id?: number | null
digest?: string
title?: string
}
const { getValues, setValue, handleSubmit, control,
formState: { errors }} = useForm<FormData>();
function setFormValues(setValue: UseFormSetValue<FormData>, lid: number, data: NewsItem) {
setValue("lid", lid);
setValue("lang_id", data.lang_id);
setValue("town_module_id", data.town_module_id);
setValue("region_module_id", data.region_module_id);
}
The FormData and the NewsItem struct have the lang_id etc. fields, and also the other structs I would like to use it with.
My attempt to make this function generic:
export type BasicFormData = {
lid: number
lang_id: number,
source_id?: number | string | null
town_module_id?: number | null
region_module_id?: number | null
}
static setFormValues<D extends BasicFormData,I extends BasicFormData>(setValue: UseFormSetValue<D>, lid: number, data: I) {
setValue(<Path<D>>"lid", lid);
setValue("lang_id", data.lang_id);
setValue("town_module_id", data.town_module_id);
setValue("region_module_id", data.region_module_id);
}
I tried to put <Path> before "lid" and it is ok for the linter
but, for the lid value the linter says:
Argument of type 'number' is not assignable to parameter of type 'PathValue<D, Path>'.ts(2345)
and:
Argument of type '"lang_id"' is not assignable to parameter of type 'Path'.ts(2345)
How can i resolve this problem?

F# nested records

Nested records
Are records similar to dictionaries where it's a tree of objects with names?
Or records are just a list of simple types
let r = { b = { a = 2 } } // is this possible? if not, how to achieve? is this useful in f#?
For me with discriminated unions it's possible to achieve kind of similar behavior (code below)
// Discriminated union for a card's suit
type Suit = | Heart | Diamond | Spade | Club
let suits = [Heart; Diamond; Spade; Club]
suits
// Discriminated union for playing cards
type PlayingCard =
| Ace of Suit
| King of Suit
| Queen of Suit
| Jack of Suit
| ValueCard of int * Suit
// generating a deck of cards
let deckOfCards =
[
for suit in [Spade; Club; Heart; Diamond] do
yield Ace(suit)
yield King(suit)
yield Queen(suit)
yield Jack(suit)
for value in 2..10 do
yield ValueCard(value, suit)
]
It's kind of similar to a dictionary in python or idk. The code below is dummy
type Customer =
{
FirstName : string
Contacts =
{
WorkPhone : string
MobilePhone : string
}
}
Nested types can be created using anonymous records:
type Customer =
{
FirstName : string
Contacts :
{|
WorkPhone : string
MobilePhone : string
|}
}
let customer =
{
FirstName = "John"
Contacts =
{|
WorkPhone = "123-456-7890"
MobilePhone = "234-567-8901"
|}
}
You can see some patterns in the code, but records are not similar to dictionaries. You can think of them as of classes rather, with strongly typed public properties. If you need to create a dictionary, you have to use one of the available map/dictionary classes or implement your own. Have a look at the Map type for example.
https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-fsharpmap-2.html
type Contact =
{
WorkPhone : string
MobilePhone : string
}
type Customer =
{
FirstName : string
Contacts : Contact
}
let cust : Customer =
{
FirstName = "Joe"
Contacts = { WorkPhone="1800131313"; MobilePhone="0863331311" }
}
The code above shows that you can nest the record types. Aside of using anonymous records as #brianberns suggested, you can declare the data types you plan to nest.
Yes, you can have nested records, but just like in your example with discriminated unions, you need to give a name to the nested type:
type CustomerConracts =
{
WorkPhone : string
MobilePhone : string
}
type Customer =
{
FirstName : string
Contacts: CustomerConracts
}
let c = { FirstName = "John", Contacts = { WorkPhone = "123", Mobile phone = "098" } }

Recursively Create ReadOnly Object in FlowJS

In redux, the state should be immutable. I would like Flow to prevent anyone from mutating that state. So, given an object of arbitrary depth:
type object = {
a: {
b: {
d: string
}
},
c: number
}
How can I create a new type that is recursively readonly, so that I cannot do:
let TestFunction = (param: $RecursiveReadOnly<object>) => {
param.a.b.d = 'some string'
}
The builtin $ReadOnly utility of Flow will create a type like this, which isn't what is needed, because b & d are still writable:
{
+a: {
b: {
d: string
}
},
+c: number
}
I've been trying to use the $Call & $ObjMap(i), but I can't figure out how to recursively travel an object in Flow. The objective is to have this:
{
+a: {
+b: {
+d: string
}
},
+c: number
}
Thanks to kalley for his solution. From what I understood, kalley tried to make any object received by a function recursively read only. Since I really only needed known objects as parameters, this works perfectly:
// Type definition that works with arbitrary nested objects/arrays etc.
declare type RecursiveReadOnly<O: Object> = $ReadOnly<$ObjMap<O, typeof makeRecursive>>
declare type RecursiveReadOnlyArray<O: Object> = $ReadOnlyArray<$ReadOnly<$ObjMap<O, typeof makeRecursive>>>
type Recursive<O: Object> = $ObjMap<O, typeof makeRecursive>
declare function makeRecursive<F: Function>(F): F
declare function makeRecursive<A: Object[]>(A): $ReadOnlyArray<$ReadOnly<Recursive<$ElementType<A, number>>>>
declare function makeRecursive<O: Object>(O): RecursiveReadOnly<O>
declare function makeRecursive<I: string[] | boolean[] | number[]>(I): $ReadOnlyArray<$ElementType<I, number>>
declare function makeRecursive<I: string | boolean | number | void | null>(I): I
// Usage example.
type obj = {
a: {
b: {
d: string,
}
}
}
let TestFunction = (param: RecursiveReadOnly<obj>) => {
param.a.b.d = 'some string' // Flow throws an error
}

How should a disjoint union wrapper be structured for refinement to work?

On the Flowtype "Try" site
// #flow
type One = {type: "One"};
type Two = {type: "Two"};
type Node = One | Two;
class Foo<N: Node> {
node: N;
constructor(n: N) {
this.node = n;
}
}
const fooNode: Foo<Node> = new Foo({type: "One"});
if (fooNode.node.type === "One") {
const fooOne: Foo<One> = fooNode;
}
the if type check is not enough to refine the type, if I understand right, because the type is not guaranteed to be constant.
Since I want to avoid the possibility of this being an X/Y problem, the usecase I'm playing with at the moment is searching from a given node with a .find method that would return the refined type, e.g. using
parent(): Foo<N> | null {
// ...
return null;
}
find<U: Node>(callback: (foo: Foo<N>) => Foo<U> | null): Foo<U> | null {
let p = this;
do {
const result = callback(p);
if (result) return result;
p = p.parent();
} while (p);
return null;
}
with
const f: Foo<Node> = new Foo({type: "One"});
const result: Foo<Two>|null = f.find((p) => p.node.type === "Two" ? p : null);
which would allow me to return the refined type at the while searching.
The problem is with the type annotation on this line:
const fooNode: Foo<Node> = new Foo({type: "One"});
By explicitly using Foo<Node> you're preventing the refinement from happening. You can use Foo<*> to make the inference work correctly.
Here's an example:
https://flowtype.org/try/#0PTAEAEDMBsHsHcBQiAuBPADgU1AeQHY4C8oA3qOtgFygBEBWtoAvgNyqY4Aq8soJ5Slhq0esJmw7ZQAOVgATYnkKgAPqDHtEAY2gBDAM4HQAMViwAPDJpzFAPjKJQofAuGytz7bHwGUAJwBXbRRYfwAKfBsASkdnZxQACwBLAwA6V0V+F3ZnZkR85G9fFFBIc1t3M0sAKgcSQnhTc3DSIREGWmZo9nDy2EqaaosGOx7EPoq3IfMLMTHWUBBQLH9-MKA
There are two problems. Foo is invariant, so you will never be able to refine it: it doesn't have any known subtypes other than itself.
On the other hand, even if Foo was covariant, it wouldn't work. You simply can't refine a generic class.
The only practical option is to unwrap, refine, and wrap again.

Reflection and typeChecking for optionals

Playing with reflections in swift 2.0 i'm trying to type check a child value.
The problem: each element of the children array in the Mirror of Any item is not optional, but his type can be optional... What happens is that of course i have the child value even if the value is nil
Maybe it is not clear so i put here some code to explain better.
For convenience i defined a subscript in a Mirror extension that fetches the child object with a given label
extension Mirror {
public subscript(key: String)->Child?{
var child = children.filter {
var valid = false
if let label = $0.label {
valid = label == key
}
return valid
}.last
if child == nil,
let superMirror = superclassMirror() {
child = superMirror[key]
}
return child
}
}
perfect, now let's say i have this class
class Rule: NSObject, AProtocol {
var hello: String?
var subRule: Rule?
}
Ok, now the problem
let aRule = Rule()
let mirroredRule = Mirror(reflecting:aRule)
if let child = mirroredRule["subRule"] {
//child.value always exists
//i can't do child.value is AProtocol? because child.value is not optional
//child.value is AProtocol of course returns false
//child.dynamicType is Optional(Rule)
if let unwrapped = unwrap(child.value) where unwrapped is AProtocol {
//This of course works only if child.value is not nil
//so the unwrap function returns an unwrapped value
//this is not a definitive solution
}
}
child.value has not been initialized so it is nil, and i can't check his type using the unwrap function. I'm writing a deserializer so i need to check the var also if it is nil because in the dictionary that will be used for the deserialization it could be defined.
private func unwrap(subject: Any) -> Any? {
var value: Any?
let mirrored = Mirror(reflecting:subject)
if mirrored.displayStyle != .Optional {
value = subject
} else if let firstChild = mirrored.children.first {
value = firstChild.value
}
return value
}
I hope the problem is clear. Any suggestions?
Based on this answer, I recommend using if case Optional<Any>.some(_).
I did something recently to make sure I have at least one optional set on my struct. You can paste into playgrounds:
struct ErrorResponse: Codable {
let message: String?
let authorizationException: [String: String]?
let validationException: String?
let generalException: String?
var isValid: Bool {
var hasAtLeastOneNonNilErrorValue = false
Mirror(reflecting: self).children.forEach {
if case Optional<Any>.some(_) = $0.value {
hasAtLeastOneNonNilErrorValue = true
}
}
return hasAtLeastOneNonNilErrorValue
}
}
let errorTest = ErrorResponse(message: "some message", authorizationException: nil, validationException: nil, generalException: nil)
let errorTest2 = ErrorResponse(message: nil, authorizationException: nil, validationException: nil, generalException: nil)
print("is valid: \(errorTest.isValid)") //is valid: true
print("is valid: \(errorTest2.isValid)") //is valid: false

Resources