Flow - 'remembering' that if X is truthy, Y must be truthy - flowtype

Is is possible to get something like this working in flow?
const flag: boolean = false
const obj: ?string = flag ? "hello" : null
if (flag) {
(obj: string) // Cannot cast `obj` to string because null or undefined is incompatible with string.
}
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVBjOA7AzgFzAA8AuMAIzjhgFMBDbMAXjCjplxsxwLDnIBWZAPwEATgEtsAc2bEwwsACIAFjRjwlYMtgCuG9BKhgAFEQCUYAN6owp-kLDip086gC+QA
I've got a variable obj that's conditionally set if flag is true. Can I do anything so that flow 'remembers' that if flag==true, obj has been set?

You can cast if you want to, but you are required to test against all other types first.
It's also recommended that you create a custom type for this, but I don't believe that is required here.
Here's a code example:
/* #flow */
type ObjectOrString = {}|string|null;
const x: boolean = false
const constObj:ObjectOrString = x ? "hello" : null
var obj:ObjectOrString = x ? "hello" : null
if ( obj && typeof obj === 'object' ) {
let o = (obj:{});
}
if ( obj && typeof obj === 'string' ) {
let o = (obj:string);
}
if ( constObj && typeof constObj === 'object' ) {
let o = (constObj:{});
}
if ( constObj && typeof constObj === 'string' ) {
let o = (constObj:string);
}
https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDyARgFZ4DGGhATgMoY0CWAdgOZgC8YA3gL4AfAM6NWbASwCuMGAG50FOCxFgAHgC4wxOHBh4Ahiy5go+mELypFyjGGsiSpdY8rV6o9sdVgA-GABEABZ4MnD+YJpSMqgAbvo0YHBkzmSutAzMntzefkEh8OGR0jDoTFBgABSJZGAAZLVg2Phw5UmkXJzcAORtrl1gAJS8qGBgerZwxhVt6vwD8nyoZZXV7fWNuHgtqx3dIpls-UM8I2N4E1Mz+2LzqItL5VX21DXrTVvlz467YD2pVEdhqNxokpl9knMFg8VuC1g13ttYT8utd2ICTsDzqDuBVYepUWxbvdUEA

Related

How can i write(=update) Localstorage object in Recoil Atom?

I try to get LocalStorge value for update Recoil atom object value below code.
const LoginState = atom({
key: 'login',
default : null
});
const Home: NextPage = () => {
const [login, setLogin] = useRecoilState(LoginState)
useEffect(()=>{
let localData = localStorage.getItem("token")
if(login === null || undefined){
if(localData !== null || undefined){
setLogin(localData)
}else{
setLogin(null)
}
}
}, [login])
but it has error like this.
Argument of type 'string | null' is not assignable to parameter of type '((currVal: null) => null) | null'.
Type 'string' is not assignable to type '((currVal: null) => null) | null'**.ts(2345)**
<br>
i reckon this problem is came from type. As far as I know type of localStorage object is string or null How can i solve it?
I found solution when i write question.
atom object need to type. First, you define type that uses in atom
type loginState = {
localValue : string | null
}
Second, add type that was defined to atom object like this
const LoginState = atom<loginState>({
key: 'login',
default : {
localValue : null
}
});
Last, you can fix according to your type
useEffect(()=>{
let localData = localStorage.getItem("token")
if(login === null || undefined){
if(localData !== null || undefined){
setLogin({localValue : localData})
}else{
setLogin({localValue : null})
}
}
}, [login])

Problem narrowing multiple properties with flow type

i'm wondering why the following code isn't valid in flow.
Flowtyped try link
/* #flow */
type Foo = {
bar: string
}
type Data = {
acme: null | { nodes: Foo[] },
beta: null | { nodes: Foo[] }
}
function a(data: Data|null) {
if (
!data ||
!data.acme ||
!data.acme.nodes ||
!data.beta ||
!data.beta.nodes
) {
return
}
const filteredAcme: Foo[] = data.acme.nodes.filter(Boolean);
const filteredBeta: Foo[] = data.beta.nodes.filter(Boolean); // <-- error on this line
// Cannot get `data.beta.nodes` because property `nodes` is missing in null [1].
}
it seems like the if statement should narrow the types so that we know beta.nodes are present.
It should, but current version of flow poorly detect presents checked on the object properties, creating local variables should help. Like the following:
Check in Flow Try
/* #flow */
type Foo = {
bar: string
}
type Data = {
acme: null | { nodes: Foo[] },
beta: null | { nodes: Foo[] }
}
function a(data: Data|null) {
if (!data) return
const {beta, acme} = data;
if (!beta || !beta.nodes || !acme || !acme.nodes) return ;
const filteredAcme: Foo[] = acme.nodes.filter(Boolean);
const filteredBeta: Foo[] = beta.nodes.filter(Boolean); // <-- ???
}
function b(data: Data|null) {
if (!data) return
if (!data.acme) return
if (!data.acme.nodes) return
const filteredAcme: Foo[] = data.acme.nodes.filter(Boolean);
if (!data.beta) return
if (!data.beta.nodes) return
const filteredBeta: Foo[] = data.beta.nodes.filter(Boolean);
}
function c(data: Data|null) {
if (!data) return
if (!data.acme) return
if (!data.acme.nodes) return
const {beta} = data;
if (!beta) return
if (!beta.nodes) return
const filteredAcme: Foo[] = data.acme.nodes.filter(Boolean);
const filteredBeta: Foo[] = beta.nodes.filter(Boolean);
}

Vue.js - Update computed property after async computed property gets updated

I have a computed property (filteredSyms) that depends on the asynchronous computed property (allSynonyms). I am using async-computed plugin for this:
https://www.npmjs.com/package/vue-async-computed.
However, when the data gets updated the computed property doesn't wait until the result of the async property update. Therefore, I receive not up to date information. Then after the async property actually return new value computed property doesn't run update again.
How can I make it work the way that computer property waits until there is a result from the async computed property?
The code is below:
asyncComputed: {
async allSynonyms() {
let allSyns = await this.$axios.$post('/db/sym/synonyms', this.model.syms);
return allSyns;
}
},
computed: {
filteredSyms() {
let that = this;
let allSyn = this.allSynonyms;
let exactMatch = this.symsByRating.filter(
function (v) {
let isExactMatch = v.title.toLocaleLowerCase().indexOf(that.searchString.toLocaleLowerCase()) >= 0;
return !that.idsToFilter.includes(v.id) && isExactMatch
&& (!that.currentBodyPart || v.bodyParts.indexOf(that.currentBodyPart) >= 0)
&& that.hasMoreSubsyms(v)
&& (!allSyn || !that.containsObject(v, allSyn))
&& (v.sex == that.model.sex || v.sex == 'NA');
});
let partialList = [];
exactMatch.forEach(ex => partialList.push({n: 100, sym: ex}));
for (let sym of this.symsByRating ) {
let searchWords = this.searchString.toLocaleLowerCase().split(' ');
let symWords = sym.title.toLocaleLowerCase().split(' ');
let n = 0;
let isPartialMatch = false;
symLoop:for (let symWord of symWords) {
symWord = symWord.substring(0, symWord.length - 1);
for (let searchWord of searchWords) {
// don't count last letters of the words
searchWord = searchWord.substring(0, searchWord.length - 1);
if (searchWord.length > 2 && symWord.indexOf(searchWord) >= 0) {
n++;
isPartialMatch = true;
}
}
}
if (exactMatch.indexOf(sym) < 0 && isPartialMatch
&& (!this.currentBodyPart || sym.bodyParts.indexOf(this.currentBodyPart) >= 0)
&& this.hasMoreSubsyms(sym)
&& (!allSyn || !this.containsObject(sym, allSyn))
&& (sym.sex == that.model.sex || sym.sex == 'NA')) {
partialList.push({n: n, sym: sym});
}
}
partialList.sort(function(obj1, obj2) {
return obj2.n - obj1.n;
});
if (this.searchString && this.searchString != '') {
partialList = this.filterSynonyms(partialList);
}
let fs = partialList.map(ws => ws.sym);
console.dir(fs);
return fs;
}
}
A lot of stuff is going on the filtered method, but I guess the main point here that it is using this.allSynonyms to do the check but it is not updated at the time filteredSyms is executed.
Thanks for your suggestions!
(I haven't really tested this out, but it should work.)
vue-async-computed does provide the status in this.$asyncComputed.allSynonyms.success.
try adding this.$asyncComputed.allSynonyms.success as a dependencies to filteredSyms and it should update when success state change.

Flowtype: How to create type guard function?

I want to use type refinements from function.
How to create type guard function (TypeScript) in flow?
I TypeScript example:
function isString(arg: Showable): arg is string {
return typeof arg === 'string';
}
II Flow
/* #flow */
type Showable = number | string;
// ok
function barOk (arg: Showable) {
return typeof arg === 'string' ? arg.length : (arg + 1);
}
// type guard function
function isString(arg: Showable) {
return typeof arg === 'string';
}
// Error
function barError (arg: Showable) {
return isString(arg) ? arg.length : (arg + 1);
// ^ Cannot get `arg.length` because property `length` is missing in `Number`
}
Change your isString function to the following:
function isString(arg: Showable): boolean %checks {
return typeof arg === 'string';
}
See Predicate Functions

flow fails on union type even with if/else

In the flow documentation, it states about typeof "This type test is particularly useful in conjunction with union types." The following, however does not pass flow's scythe:
var EventEmitter = require('events').EventEmitter;
var fnify = function(key: string | (x: number, y: any) => string) {
var fnkey = typeof(key) === 'function' ? key : (t) => key;
new EventEmitter().emit(fnkey(0), 0);
}
Flow complains that it does not know the return value of fnkey, which is odd, as it is guaranteed to be a string from the signature of the function. What does go through is:
var EventEmitter = require('events').EventEmitter;
var fnify = function(key: string | (x: number, y: any) => string) {
var fnkey = typeof(key) === 'function'
? key
: (t) => typeof(key) === 'string' ? key : null;
var kludge = fnkey(0);
if (kludge) {
new EventEmitter().emit(kludge, 0);
}
}
But the latter seems unnecessarily verbose. Is this a feature? Bug? Is there something wrong in the first snippet that makes flow irate?
The problem is that key can change in the function body, either use a const binding
var EventEmitter = require('events').EventEmitter;
var fnify = function(key: string | (x: number, y: any) => string) {
const k = key;
var fnkey = typeof(k) === 'function' ? k : (t) => k;
new EventEmitter().emit(fnkey(0), 0);
}
or set experimental.const_params=true in the [option] section of your .flowconfig

Resources