Typing object of functions with flowtype - flowtype

I understand that this is the type of an object, which keys are the two strings 'foo' and 'bar', and the values two functions (although I find the syntax strange):
type Func0 = {
foo(number) : number,
bar(string) : string
};
for example:
const f: Func0 = {
foo: x => 2*x,
bar: x => `hello ${x}`
};
But what is this type? The type of an object with two functions as values? If so, what are the keys?
type Func1 = {
(number) : number,
(string) : string
}

Yes, this is the callable object with two possible strict outcomes. Repro example here - try flow.
In brief,
type fn = {
(number): number,
(string): string
}
in pseudo-code is:
type fn =
(number) => number
OR
(string) => string
so:
fn(number): number
fn(string): string
While with default function declaration you cannot keep the relation between parameter type and return type:
type fn = (number | string) => number | string
in pseudo-code is:
type fn = (number OR string) => number OR string
so:
fn(number): number | string
fn(string): number | string

Related

Realm object with dictionary of [String: Any] to save the JSON value in the property

Player Object Model
In the Player Model, I want to save the JSON response so that I will get any new computed properties in the future without changing the schema.
But here, I'm getting the error to save the json of type [String: Any].
Any alternative or recommendations...?
Any is not a supported value type of Map. Looking a the documentation for Map, which shows the definition
public final class Map<Key, Value>
value is a RealmCollectionValue can be one of the following types
This can be either an Object subclass or one of the following types:
Bool, Int, Int8, Int16, Int32, Int64, Float, Double, String, Data,
Date, Decimal128, and ObjectId (and their optional versions)
One option is to to use AnyRealmValue so it would look like this
class Player: Object {
let json = Map<String, AnyRealmValue>()
}
here's how to populate the json with a string and an int
let s: AnyRealmValue = .string("Hello")
let i: AnyRealmValue = .int(100)
let p = Player()
p.json["key 0"] = s
p.json["key 1"] = i
then to get back the values stored in the map:
for key in p.json {
let v = key.value
if let s = v.stringValue {
print("it's a string: \(s)")
} else if let i = v.intValue {
print("it's an int: \(i)")
}
}
and the output
it's a string: Hello
it's an int: 100

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?

Flow. How to specify the union type depending on the function argument

Let's say we have two types and union of them:
type A = {tag: 'a'};
type B = {tag: 'b'};
type U = A | B;
And function, which returns A or B, depending on provided tag:
const getSpecificObj = (tag: string) => U;
How can I specify that returning type? So then I will be able to use it like:
const a: A = getSpecificObj('a');
P.S. Yes, I know that I can write the same function for every type or override. But maybe there are another ways. For example in TS I can write:
type Extract<A, T> = A extends { tag: T } ? A : never;
<T>(tag: T): Extract<U, T>
Rather than returning a union of the types A and B, you need to parameterize the function getSpecificObj by the type of its argument, and then return an object which references that type parameter. Here's how I would do it:
type A = {tag: 'a'};
type B = {tag: 'b'};
function getSpecificObj<T : string>(tag: T) : { tag: T } {
return { tag };
}
const a : A = getSpecificObj('a');
const b : B = getSpecificObj('b');
The annotation <T : string> on getSpecificObj makes sure the function isn't more general than you need, and will only accept strings as tags. You can think of this as moving the string annotation off of the tag parameter and into the generic type of the tag parameter.
You can view the example on the try-flow page here.

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
}

Get string from userInfo Dictionary

I have a userInfo dictionary from a UILocalNotification. Is there an easy way to get the String value when using implicit unwrapping?
if let s = userInfo?["ID"]
Gives me a AnyObject that I have to cast to a string.
if let s = userInfo?["ID"] as String
Gives me an error about StringLiteralConvertable
Just didn't want to have to declare two variables to get a string - one literal for the unwrap and another var for the casted string.
Edit
Here is my method. This doesn't work either - I get (NSObject, AnyObject) is not convertible to String on the if statement.
for notification in scheduledNotifications
{
// optional chainging
let userInfo = notification.userInfo
if let id = userInfo?[ "ID" ] as? String
{
println( "Id found: " + id )
}
else
{
println( "ID not found" )
}
}
I don't have it in my question, but besides getting this way to work, I'd like to actually have
if let s = notification.userInfo?["ID"] as String
You want to use a condition cast using as?:
(Note: This works for Xcode 6.1. For Xcode 6.0, see below)
if let s = userInfo?["ID"] as? String {
// When we get here, we know "ID" is a valid key
// and that the value is a String.
}
This construct safely extracts a string from userInfo:
If userInfo is nil, userInfo?["ID"] returns nil due to optional chaining and the conditional cast returns a variable of type String? that has a value of nil. The optional binding then fails and the block is not entered.
If "ID" is not a valid key in the dictionary, userInfo?["ID"] returns nil and it proceeds like the previous case.
If the value is another type (like Int), then the conditional cast as? will return nil and it proceeds like the above cases.
Finally, if userInfo is not nil, and "ID" is a valid key in the dictionary, and the type of the value is a String, then the conditional cast returns an optional string String? containing the string. The optional binding if let then unwraps the String and assigns it to s which will have type String.
For Xcode 6.0, there is one additional thing you must do. You need to conditionally cast to NSString instead of to String because NSString is an object type and String is not. They apparently improved that handling in Xcode 6.1, but for Xcode 6.0 do the following:
if let s:String = userInfo?["ID"] as? NSString {
// When we get here, we know "ID" is a valid key
// and that the value is a String.
}
Finally, addressing your last point:
for notification in scheduledNotifications
{
if let id:String = notification.userInfo?["ID"] as? NSString
{
println( "Id found: " + id )
}
else
{
println( "ID not found" )
}
}

Resources