Typescripting custom plugins in Nuxt - javascript

In my Nuxt project, I created a custom plugin file that returns an object. /helpers/settings:
export const settings = {
baseURL: 'https://my-site.com',
...
};
I register this file in /plugins/settings.ts:
import Vue from 'vue';
import { settings } from '~/helpers/settings';
Vue.prototype.$settings = settings;
And in nuxt.config.js:
export default {
...
plugins: [
'~/plugins/settings',
Then, in a component, I can use my plugin like so:
export default Vue.extend({
data() {
return {
url: `${this.$settings.baseURL}/some-path`,
Everything works as expected, except in that in my console, I get a typescript error from the line that I refer to my plugin in my component:
Property '$settings' does not exist on type 'CombinedVueInstance<Vue, unknown, unknown, unknown, Readonly<Record<never, any>>>'.
Hence my question: what is the proper way to apply a type to my custom plugin so that I don't get this error every time I use it?

According to the docs, you'll need to augment the type file for Vue.
Place the following code in a file named plugin-types.d.ts.
// 1. Make sure to import 'vue' before declaring augmented types
import Vue from 'vue'
// 2. Specify a file with the types you want to augment
// Vue has the constructor type in types/vue.d.ts
declare module 'vue/types/vue' {
// 3. Declare augmentation for Vue
interface Vue {
$settings: string
}
}

This answer also work but I found an easier, though dirtier, way to fix it without the need of adding the plugin-types.d.ts file.
Just add a property to you componen with the name of plugin like following:
#Component
export default class YourComponent extends Vue {
private $settings!: string; // your plugin here
private url: string = `${this.$settings.baseURL}/some-path`
}

Related

Using vue.use in a library own

I'm using the vue-sfc-rollup lib to create a lib for Vue, so far it's ok, my lib is working, and I can use it in other projects, however, a doubt has arisen.
In which file could I add in my lib the use of bootstrap? to understand better I'm putting below the structure that the SFC generated for me..
The file entry.js
// iife/cjs usage extends esm default export - so import it all
import plugin, * as components from '#/entry.esm';
// Attach named exports directly to plugin. IIFE/CJS will
// only expose one global var, with component exports exposed as properties of
// that global var (eg. plugin.component)
Object.entries(components).forEach(([componentName, component]) => {
if (componentName !== 'default') {
plugin[componentName] = component;
}
});
export default plugin;
the file entry.esm.js
// Import vue components
import * as components from '#/lib-components/index';
// install function executed by Vue.use()
const install = function installZicketVueCheckout(Vue) {
Object.entries(components).forEach(([componentName, component]) => {
Vue.component(componentName, component);
});
};
// Create module definition for Vue.use()
export default install;
// To allow individual component use, export components
// each can be registered via Vue.component()
export * from '#/lib-components/index';

how to use Flow with external Higher Order Component Wrapper

Flow documentation only shows how to declare a custom Higher Order Component to work with custom Class and its Props. In my case I have a custom Class like:
type Props = {
navigation: Object,
isFocused: boolean
}
type State = {
config: AppConfig,
pack: Package,
init: boolean,
}
class MainAppScreen extends React.Component<Props, State> {
...
}
export default withNavigationFocus(MainAppScreen);
and want to wrap it with external HOC from 'react-navigation';
What should I do (beside //$FlowFixMe) to get rid of this message:
Error:(111, 16) Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. (signature-verification-failure)
Thanks.
UPDATE:
As #user11307804 pointed into the right direction, I have been also trying involving flow-typs for external libraries. However it still seems not to be possible to import a type for a (HOC) function like:
declare export function withNavigationFocus<Props: {...}, ComponentType: React$ComponentType<Props>>(
Component: ComponentType
): React$ComponentType<$Diff<React$ElementConfig<ComponentType>, {| isFocused: ?boolean |}>>;
When I try to import it like: (following this example)
import type {withNavigationFocus} from 'react-navigation';
I get the error:
Error:(22, 14) Cannot import the value `withNavigationFocus` as a type. `import type` only works on type exports like type aliases, interfaces, and classes. If you intended to import the type of a value use `import typeof` instead.
and if I try with typeof I get:
import typeof {withNavigationFocus} from 'react-navigation';
I get the errors:
Error:(22, 16) Cannot declare `withNavigationFocus` [1] because the name is already bound.
Error:(112, 16) Cannot call `withNavigationFocus` because Named import from module `react-navigation` [1] is not a function.
Error:(112, 16) Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. (`signature-verification-failure`)
Thanks.
Flow is complaining that withNavigationFocused is untyped. Fortunately, the flow-typed project has react-navigation types. (There are other definition files for different version of react-navigation or flow; the one I've linked is for react-navigation^4.0.0 and flow^0.114.0.) You can include the library definition in your project following the Library Definitions documentation (essentially, just save the file in <PROJECT_ROOT>/flow-typed directory).

Pulling in types for use in a .d.ts file with declare module without making it a module file

I am attempting to write a type declaration for an NPM package (or more specifically an untyped directory within a package) my project depends on.
The package itself is react-big-calendar and it doesn't bundle its own types, however there is #types/react-big-calendar which provides types for the main package, but not for the react-big-calendar/lib/addons/dragAndDrop "sub-package" it has in itself.
The above gets me working import BigCalendar from 'react-big-calendar' which is great, and I want to also get working import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop' so I figured I'd just declare module my way there.
I cannot place the declare module statement in any TSX file, because it has to be in its own file which is not an ES module, but it also cannot be an import+export free TS file, because I am also using CRA which enforces isolatedModules and so disallows non-module TS/X files.
I can and should place it in a .d.ts file, like this:
declare module 'react-big-calendar/lib/addons/dragAndDrop' {
function withDragAndDrop(calendar: any): any;
export = withDragAndDrop;
}
This looks fine, but is not much of an improvement typing-wise. The function I am looking to type basically takes a React component and returns it with some extra props. But even to just type is as a function which takes the specific BigCalendar component and returns it is a problem, because I cannot use an import statement (to pull in the component type) in the d.ts file. If I do, it turns into a module file and that breaks the declare module statement.
I am looking for something like this:
declare module 'react-big-calendar/lib/addons/dragAndDrop' {
function withDragAndDrop(calendar: BigCalendar): typeof BigCalendar & {
props: {
extraProp1: string;
// …
extraPropN: string;
}
};
export = withDragAndDrop;
}
With that I should be able to use the HOC like this: const DragAndDropCalendar = withDragAndDrop(BigCalendar); followed by <DragAndDropCalendar originalProp={value} extraProp1={value} />.
The thing that is missing is pulling in the types to the .d.ts file in a way which doesn't turn it into a module breaking the declare module statement stripping me of types, bringing me to square one again.
What options do I have there? I tried to use require but that returns any and I couldn't figure out if <reference is the right tool here or not.
I figured out how to import the original component types (React Big Calendar in this case, but the solution is generic) in the typings (which in this case are for the RBC drag and drop addon).
withDragAndDrop.d.ts:
declare module 'react-big-calendar/lib/addons/dragAndDrop' {
import BigCalendar, { BigCalendarProps, Event } from 'react-big-calendar';
type withDragAndDropProps<TEvent> = {
onEventDrop: (args: { event: TEvent, start: stringOrDate, end: stringOrDate, allDay: boolean }) => void;
onEventResize: (args: { event: TEvent, start: stringOrDate, end: stringOrDate, allDay: boolean }) => void;
};
declare class DragAndDropCalendar<TEvent extends Event = Event, TResource extends object = object>
extends React.Component<BigCalendarProps<TEvent, TResource> & withDragAndDropProps<TEvent>>, {}
function withDragAndDrop(calendar: typeof BigCalendar): typeof DragAndDropCalendar;
export = withDragAndDrop;
};
Usage:
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";
const DragAndDropCalendar = withDragAndDrop(BigCalendar);
// TSX:
<DragAndDropCalendar<MyEvent> … onEventDrop onEventResize />

How to design a global class for storing settings in Angular?

I have added a class like this.
export class Settings{
public data: string = "blopp";
}
When I try to access the data, it seems that the field I'm trying to assign that value to sees the class Settings itself but it doesn't recognize the data thingy.
How do I redesign the class to provide settings for other components?
I've read about #Output decorator but since I won't be binding to the values, it seems not the correct approach. I've made sure that the class is imported and recognized withing the component that's supposed to consume it. I've also tried the corresponding exposure but using a function in the class with settings - the same, failed result.
If you're using angular-cli and going to store in this class environment specific settings - you already have built in support for this.
Put the setting into environment.ts. For example:
export const environment = {
production: false,
someSetting: 'foo',
};
Then it can be consumed from anywhere within the app:
import { environment } from "../environments/environment"; //fix according to your project structure
#Injectable()
export class SampleService {
private foo = environment.someSetting;
}
Here you can find more info on how to add more environments and build you project with specific environment settings.
Your best bet for storing global settings is to use a service. The code for that looks like this:
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
serviceData: string;
}
I have a blog post about this here: https://blogs.msmvps.com/deborahk/build-a-simple-angular-service-to-share-data/
And a plunker here: https://plnkr.co/edit/KT4JLmpcwGBM2xdZQeI9?p=preview
Also, the #Output decorator is only for communication between a child component and a parent component where the child is nested within the parent.

Invoke JQuery method from Angular 2 component

I'm working on creating an Angular 2 front end on an existing project that previously just used JQuery. Is it possible to invoke a JQuery method inside of an Angular 2 component, when that JQuery function exists in a separate file? I'm writing my components in TypeScript, in case that is important to know.
For example, I have a JavaScript file called EditCheckBoxes.js with a method called editCheckBoxes(). In order for editCheckBoxes() to work, I need it to be invoked after a certain component is initiated. My attempted solution was this:
ngOnInit(): void {
editCheckBoxes();
}
This code gives me the following error:
Cannot find name 'editCheckBoxes'.
Is there any way I can get this to work?
Also, I added declare var $: any; to my component file, so I am able to use JQuery within that component, but I'd rather not copy entire files into my components in order to use them.
EDIT:
The folder structure looks like this:
Plan
app
selling
My Angular 2 component
Scripts
EditCheckBoxes.js
In my component, the import statment looks like this: import { editCheckboxes } from '../../Scripts/EditCheckboxes';
You need to import the editCheckboxes method in your typescript file for it to be available.
To do that you first need to export the editCheckboxes function from EditCheckBoxes.js
export function editCheckboxes() { ... }
Next you should just import that function inside your component
import { editCheckBoxes } from './EditCheckBoxes';
Now it can be called in your component: editCheckBoxes();
In order to import function from js file you should at first export it like so:
// EditCheckBoxes.js
module.exports = function editCheckBoxes () { ... };
then add a d.ts file in same directory as your js-file with definition of your module that would be used by Typescript
// EditCheckBoxes.d.ts
declare function editCheckBoxes (): void;
export = editCheckBoxes;
then in your Typescript file you would specify your definition file, import your function and use it like so:
/// <reference path="../../Scripts/EditCheckBoxes.d.ts" />
import editCheckBoxes = require('../../Scripts/EditCheckBoxes');
ngOnInit (): void {
editCheckBoxes();
}

Categories