I'm using koa which has middleware props typed as mixed, so I'm trying to do something along the lines of the following below but I'm getting an error that Cannot call `ctx.render` because mixed [1] is not a function.Flow(not-a-function)
app.use(async (ctx, next) => {
// some other code above it
await ctx.render('index');
});
My question is, what's the correct way to do a type refinement that this is a function and then allow me to call it?
You can refine this to a function, but calling it is another matter.
app.use(async (ctx, next) => {
if (typeof ctx.render === 'function') {
// Now we know that `ctx.render` is a function.
}
});
Flow actually has a special case for this, this is called an "unknown function." We know that ctx.render is a function, but we don't know anything about its arguments or return type so we can't safely do anything with it except pass it around. How can we safely call ctx.render(1) if we don't know that ctx.render takes a number?
What's more, we can't know anything about it. There is no reflection mechanism provided by JavaScript that we could interrogate for enough information about this function to be able to safely call it. The only thing we can find is the static arity (ctx.render.length) but this by itself is not reliable or sufficient.
If we had more information, like say if this were a union type instead of mixed, then we could use type refinement to do what we want:
(arg: boolean | (number => void)) => {
if (typeof arg === 'function') {
arg(1); // safe because if this is a function, we know it takes a number
}
};
In this case the most reasonable solution is to type through any. Assuming that we know that we should only ever receive one type of render function, then we just forcibly cast it to that type with all the blaring caveats one would expect:
// I don't know what the type of your render function is, but you would put it
// here:
type RenderFunction = string => void;
app.use(async (ctx, next) => {
// some other code above it
if (typeof ctx.render === 'function') {
// DANGER: We make a hard assumption here that the render function is
// always of this specific type. If it is ever of any other type then
// behavior is undefined!
await ((ctx.render: any): RenderFunction)('index');
}
});
Also it sounds to me like the koa libdef could probably be improved upon.
Related
Here's the problem I'm having with tauri.
'return' shows you the return value I need, and I know for a fact that writing it this way does not work at all.
'pick_file' is called asynchronously, and I know that message passing seems to work, but is there an easier way to get the value I need.
#[tauri::command]
fn open_file() -> String {
dialog::FileDialogBuilder::default()
.add_filter("data", &["json"])
.pick_file(|path_buf| match path_buf {
Some(p) => return format!("{}", p.to_str().unwrap()),
_ => return "".into()
});
}
First, return in a closure returns from the closure and not from the function that contains it.
The more fundamental issue is that you can't return a String from open_file() if you use FileDialogBuilder::pick_file(). According to the documentation, pick_file() is non-blocking and returns immediately without waiting for the user to pick the file. What you can do in the closure is send the file down a channel, and pick it up elsewhere.
Is there a possibility to specify whether the action has its error field set to true?
const response = function*() {
yield takeEvery("CLIENT_RESPONSE", handleResponse);
}
However, we don't know whether the action with type CLIENT_RESPONSE has its error field set to true or not.
I know I can check this in the handleResponse but that seems to be more work than it should. For instance, the handleResponse might get complex because for both the non-error and error case I need to write a lot of code (i.e. I want to have different handlers for both cases).
So is there a way to specify to only take that action when error is set to true?
According to Saga API reference, the pattern (first argument) of takeEvery can be String, Array or Function.
You can achieve what you want by passing a function:
const response = function*() {
yield takeEvery(action => (action.type === "CLIENT_RESPONSE" && !action.error), handleResponse);
}
In the following example, since I'm using matching over type of Message using the switch statement, I would like flow to recognise my incorrect case of 'ENUM_TYPO'. It currently doesn't.
type Message = 'BROADCAST_MESSAGE' | 'PRIVATE_MESSAGE';
const message: Message = 'BROADCAST_MESSAGE';
switch (message) {
case 'ENUM_TYPO':
// Do Broadcast
break;
default:
break;
}
As of v0.32.0, Flow does not complain about unreachable code, unless it's something like
// #flow
function foo() {
throw new Error();
return 123; // This will error
}.
However, consider the following code
// #flow
function foo(x: string): Object {
if (x === 123) {
return x;
}
return {};
}
Will currently will not error on this code. Flow does in fact notice that x === 123 will never be true. Inside the if block, Flow will refine the type of x to the empty type, since it doesn't believe that this code will ever be reached. That is why it doesn't complain about the return x statement.
One of the members of the Flow team is almost done with adding reachability analysis to Flow. Once this improvement lands (I'm guessing v0.34.0?), Flow will complain when it sees a conditional that it thinks will always fail. This will help you with your example, since switch statement cases are basically strict equality checks.
I am trying to pass a value in the callback of an async meteor method. "mongoCollections" is global variable
// Async method
let waiter = function(cb) {
setTimeout(() => {
cb(undefined, {data: 'test', other: mongoCollections})
}, 1000);
}
// Meteor method
Meteor.methods({
'getCollections': () => {
let func = Meteor.wrapAsync(waiter);
let res = func();
return res;
}
});
On the client
Meteor.call('getCollections', (err, res) => {
console.log(err, res)
});
The issue is that in its current state the client callback is not fired, no error or anything.
But if I remove the "other: mongoCollections" part of the object then the callback is fired. Why would sending mongoCollections prevent the callback from being fired at all? If there is an error how can I catch it?
My guess is that you are loosing your context between waiter() and the execution of cb(), which means that mongoCollections is undefined in cb(), thus the call fails.
Try to log mongoCollections in the anonymous function that you setTimeout for. It will probably show as undefined.
Or try it like this:
let waiter = function(cb) {
var _mongoCollections = mongoCollections;
setTimeout(() => {
cb(undefined, {data: 'test', other: _mongoCollections})
}, 1000);
}
(which puts mongoCollections in the closure instsead)
Another possibility (based on comment below): Your mongoCollections object is not serializable. You can try it by logging the result of JSON.stringify(mongoCollections). If this fails you have to extract the parts of the object you need, that can be serialized.
There are a number of things that could be happening here, but my guess is that an error is occurring somewhere and the error message is getting swallowed by a handler somewhere deeper.
You probably want to be using Meteor.setTimeout instead of vanilla setTimeout at a minimum. Have a look here: http://docs.meteor.com/api/timers.html#Meteor-setTimeout
Beyond that, I would follow the previous answerer's advice and try to make sure that mongoCollections is as global as you think it is. If the only change between the callback working and not working is the addition of a single symbol, then the culprit is likely that your added symbol is undefined.
This code passes the flow check:
/* #flow */
function test (list: ?Array<string>): Promise<number> {
if(list !== null && list !== undefined) {
return Promise.resolve(list.length)
} else {
return Promise.resolve(0)
}
}
console.log(test(null))
Whereas the following gets a null check error
/* #flow */
function test (list: ?Array<string>): Promise<number> {
if(list !== null && list !== undefined) {
return Promise.resolve().then(() => list.length)
} else {
return Promise.resolve(0)
}
}
console.log(test(null))
error:
property `length`. Property cannot be accessed on possibly null value
Clearly list cannot be null so there must be something about the code structure that makes flow unable to recognise this.
I would like to understand what limitation I am hitting and how I can work around it. Thanks!
Basically, Flow doesn't know that your type refinement (null check) will hold at the time when () => list.length is executed. Inside that callback Flow only looks at the type of list – which says it can be null.
The difference between first and second snippet is that in the second snippet list is crossing a function boundary – you're using it in a different function than where you refined its type.
One solution is to extract list.length into a variable, and use that variable in the callback.
var length = list.length;
return Promise.resolve().then(() => length)
This might also work:
var list2: Array<string> = list;
return Promise.resolve().then(() => list2.length)
Note that this problem exists even for immediately invoked callbacks, e.g. when using map or forEach. There is an issue on flow's github about this, but I couldn't find it after a quick search.