TypeScript safe route names? - vue-router

Consider the following code:
const router = useRouter()
await router.push({
name: 'NonExistingRoute', // no typescript error ):
})
A non existing route name was given, but no TypeScript error happens. Instead the issue will only be noticed on runtime. Any way to get a compile-time error on this?

Perhaps you could wrap this in a utility function that only accepts typed route strings
const router = useRouter()
export type NamedRoute = "login" | "logout" | "user-profile";
export async function goToNamedRoute(name: NamedRoute): Promise<void> {
return router.push({name});
}

In short no.
For a compile error to exist there would need to be something explicitly wrong with the code, referencing an non-existent file, syntax error, etc.
It does sound like you are trying to solve some other issue here...i.e. why do you have the names of non-existing routes in your app?
In any case, perhaps you can avoid your errors programmatically, e.g.
let r = router.resolve({name: 'NonExistingRoute'});
if (r.resolved.matched.length > 0){
// exists
} else {
// doesn't exist
}

If you want to rely on Typescript for detecting wrong routes you might just use enums or closed types maybe?, although that will surely require some composition. Probably one way to go could be:
enum Cities {
NY,
London
}
function routeCreator(city: Cities, restOfPath?: string){
//combine paths somehow, e.g.
if(!restOfPath) return `/${Cities[city]}/`;
return `/${Cities[city]}/${restOfPath}`
}

Related

trace() error "can only be used inside a tracked computed value or a Reaction" when used inside computed

I've used MobX for a few years now, and love it, but sometimes my trace calls are not functioning, and I don't understand why not. There must be some fundamental thing that I've completely misunderstood, but most likely have been lucky enough to get through anyway. Here's an example of using trace() where I'm getting an error:
import { computed, observable, trace } from "mobx";
class Stat {
#observable baseValue = 1;
#computed get value() {
trace();
return this.baseValue;
}
}
const strength = new Stat();
strength.baseValue = strength.baseValue + 1;
The expected output, in my mind, is that trace reacts to the change in "baseValue" and logs the change. Instead, I'm getting the following error:
Error: [MobX] 'trace(break?)' can only be used inside a tracked computed value or a Reaction. Consider passing in the computed value or reaction explicitly
"Inside a tracked computed value" is, to my understanding, exactly what I'm doing. Or..?
Full sandbox: https://codesandbox.io/s/mobx-trace-trouble-ki2qj?file=/index.ts:0-312
As far as I understand this phrase
inside a tracked computed value or a Reaction.
you need to access computed value inside reactive context, like inside observer or reaction or autorun. Otherwise trace just don't have information about what is going on because your computed value is untracked at that moment by any observer.
So this will work:
const MyComponent = observer(() => {
return <div>{strength.value}</name>
})
or this
autorun(() => {
console.log(strength.value);
});

Stencil: Sudden build error without message after adding `#Method` to component

I cannot give too much information here (because there really isn't), but only this:
All of the sudden, after adding a #Method function to a stencil component:
#Method()
async setMenuItems(items: Element[]): Promise<void> {
// code here
}
the component stopped compiling with the following - really unhelpful - error:
[ ERROR ] ./src/components/menu-content/menu-content.tsx:63:44
build error
L62: #Method()
L63: async setMenuItems(elements: Element[]): Promise<void> {
L64: const unsupportedChildren = elements.filter(e => !this.isSupportedChild(e)).map(e => e.tagName);
[12:37.1] build failed in 7.02 s
Things to notice
the return type Promise<void> inside the error-message is highlighted red
there are other #Methods that do work within this component (even with the same return type).
the "broken" #Method is structurally equal to those that do work.
TypeScript compiler does not complain about anything
Only stencil compiler fails
I already tried...
to google for this issue - did not find any hints to this problem
to remove the async and add return Promise.resolve()
to rename the method (I mean.. why not)
to move the method to another place in class
to remove the whole method (compiles fine x( )
to remove the #Method decorator (compiled, but of course my method is removed from API)
to delete node_modules folder and reinstall
I remember that I already had this error once, and apparently I somehow fixed it (or not, idk). But if I did, I cannot remember how.
Does anyone have an idea how to debug this - or even better fix?
I figured it out. A more complete version of my component is:
import { Element, ... } from '#stencil/core';
class MenuContent {
#Element() element: MenuContentElement;
#Method()
setMenuItems(elements: Element[]): Promise<void> {
// ------------------^^^^^^^
// Element is meant to be the built-in browser interface for Element
// but stencil thinks that this is the imported Element from '#stencil/core'!
}
}
The exact problem here is, that the stencil-compiler seems to assume that the elements parameter is of type Element that is imported from #stencil/core which is obviously wrong and leads to this strange unhelpful error.
Possible Solutions
1. Use an alias type for the built-in Element type
// types.ts
export type DomElement = Element;
// menu-content.tsx
import { DomElement } from './types';
...
async setMenuItems(elements: DomElement[]): Promise<void> { ... }
2. Switch to HTMLElement
Note: This is only legit, when you don't need to support other Element-types such as SVGElements for example!
async setMenuItems(elements: HTMLElement[]): Promise<void> { ... }
3. Use alias in import statement
Please note: When using #stencil eslint rules, they will complain about your renamed import and say that "own class members are not allowed to be public".
I created a ticket for it here: https://github.com/ionic-team/stencil-eslint/issues/28
import { Element as ElementDecorator} from '#stencil/core';
class MenuContent {
// eslint will complain about that:
// "Own class properties cannot be public."
#ElementDecorator() element: HTMLMenuContentElement;
}
I experienced this same issue not with the Element type but with the Event type, so it appears to be rooted deeper - also about a year after you reported this issue it seems to still be a problem with Stencil

make a method globally available in Vue.js

In several Vue files this computed property checks if this.data is not an empty object:
computed: {
isLoaded() {
return !(this.data && Object.keys(this.data).length === 0 && this.data.constructor === Object); // checks if this.data is not empty
}
}
Then isLoaded is used to conditionally display content in the browser.
I'd like to refactor the code and create a global method somehow that can check if an object is empty so all the files that use this method can get it from a central spot.
Even after doing some reading on Vue mixins and plugins I'm not clear which one best fits this use case. Which one should be used for this? Or is there an altogether different approach that would be better to create a global method?
There are several ways to do it and it and approach depends on particular case. For your case I'd suggest you to create a separate folder for utils functions, where you could have smth like common.js. There you can just export your functions e.g.
export const emptyObj = (obj: any): any => Object.keys(obj).length === 0;
and import it in your component:
import { emptyObj } from "src/utils/common";
In this case it easier to organize your shared functions, easier to test them and have typescript support.

Unused Property WebStorm

I find the unused property warning super useful in a lot of situations, but often I get an error in the following situation:
const hello = () => 'Hello world!';
module.exports = {
hello, // <-- saying this is unused
};
I see that you can turn this feature off, but I'd rather keep it.
Export is marked unused if the exported module is not imported anywhere.
To get rid of the issue, you need importing the module in a different file, like:
const { hello } = require("./hello")

Flux architecture circular dependency

I have started learning Facebook's Flux architecture. I am trying to make a simple login screen. I have followed the flux-chat sample app to create the screen. I have a problem of circular dependency between ServerActionCreator and WebAPIUtils. Please see the code below.
ServerActionCreator.js
var AppDispatcher = require('../dispatcher/AppDispatcher');
var Constants = require('../constants/Constants');
var WebAPIUtils = require('../utils/WebAPIUtils');
var ActionTypes = Constants.ActionTypes;
module.exports = {
receiveLoginStatus: function(status){
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVE_LOGIN_STATUS,
status: status
});
},
loginSubmit: function(data){
WebAPIUtils.login(data);
}
}
WebAPIUtils.js
var ServerActionCreator = require('../actions/ServerActionCreator');
module.exports = {
login: function (data) {
//Mock server API call
var status = JSON.parse('{"status":"success"}');
ServerActionCreator.receiveLoginStatus(status);
}
};
As you can see ServerActionCreator depends on WebAPIUtils and WebAPIUtils depends ServerActionCreator.
I think, due to circular dependency WebAPIUtils becomes an empty object and I am getting "undefined is not a function" error when loginSubmit function in ServerActionCreator is called. Screenshot below.
How to handle this scenario? or Is there any alternative way? Any help is much appreciated.
Whenever you have a circular dependency between modules, a common solution is to either combine the modules or to create a third entity that will break the cycle.
In your case, I'd argue that you could move loginSubmit to a different action creators module. It's actually a user action, not a sever action, anyway. So maybe loginSubmit could go in UserActionCreators.js along with any number of other user action creator methods.
Another solution to your problem (and to circular dependencies in general) is to make your methods more pure, removing dependencies and instead passing in dependencies as arguments. So WebAPIUtils.login() could take a second argument, which would be the success callback. Thus:
WebAPIUtils.login(data, ServerActionCreator.receiveLoginStatus)