Tips for successfully typing the backtick character when transitioning to Typescript:

I am currently working on a Typescript Vue project involving Leaflet. I came across some code for lazy-loading map markers, but it was written in Javascript. Although the code works fine, I keep receiving errors and warnings from VSCode because this is not properly typed. How can I add type annotations to this?

Update 1: With @aplet123's suggestion, I managed to resolve the first type issue. However, I am stuck with the second one related to this._updateIconVisibility. It seems that this problem arises because I am extending existing functionality (i.e., _updateIconVisibility does not actually exist as a type). What should be my next step? I assume it might be best practice to create a custom class with the necessary methods, but I am unsure if using an anonymous object or another approach is more common...

L.Marker.addInitHook(function (this: L.Marker) {
    this.on(
        'add',
        function () {
            this._updateIconVisibility = function () {
                var map = this._map,
                    isVisible = map.getBounds().contains(this.getLatLng()),
                    wasVisible = this._wasVisible,
                    icon = this._icon,
                    iconParent = this._iconParent,
                    shadow = this._shadow,
                    shadowParent = this._shadowParent

                // remember parent of icon
                if (!iconParent) {
                    iconParent = this._iconParent = icon.parentNode
                }
                if (shadow && !shadowParent) {
                    shadowParent = this._shadowParent = shadow.parentNode
                }

                // add/remove from DOM on change
                if (isVisible != wasVisible) {
                    if (isVisible) {
                        iconParent.appendChild(icon)
                        if (shadow) {
                            shadowParent.appendChild(shadow)
                        }
                    } else {
                        iconParent.removeChild(icon)
                        if (shadow) {
                            shadowParent.removeChild(shadow)
                        }
                    }

                    this._wasVisible = isVisible
                }
            }

            // on map size change, remove/add icon from/to DOM
            this._map.on(
                'resize moveend zoomend',
                this._updateIconVisibility,
                this
            )
            this._updateIconVisibility()
        },
        this
    )
})

Answer №1

I have put together a solution for your code that addresses the errors and provides guidance on moving forward. While I may not be well-versed in your environment (Vue, etc.), this file serves as scaffolding to help you understand the process.

The key lies in accurately defining your this types at each stage. By creating a structure, implementing a type called MyObject, and specifying this as type MyObject where necessary, we can resolve the issues.

Please review the modified code below and feel free to ask if there are any unclear areas.

//  Additional setup to accommodate environmental requirements
namespace L {
    type handler = () => void

    type target = object;

    export type on = (types: string, handler: handler, target: target) => void

    export interface Marker {
        on: on
    }
}

const L = {
    Marker: {
        addInitHook: function(f: (this: L.Marker) => void) {
            //  Placeholder instruction based on code input
        }
    }
}

//  Introduced type for integration into your code
namespace L {
    export type MyObject = {
        _updateIconVisibility: () => void,
        _map: {
            on: on
        }
    }
}

//  Your existing code with TypeScript compiler compliance
L.Marker.addInitHook(function (this: L.Marker) {
    this.on(
        'add',
        function (this: L.MyObject) {
            this._updateIconVisibility = function () {
                var map = this._map,
                    isVisible = map.getBounds().contains(this.getLatLng()),
                    wasVisible = this._wasVisible,
                    icon = this._icon,
                    iconParent = this._iconParent,
                    shadow = this._shadow,
                    shadowParent = this._shadowParent

                // Retain parent of icon
                if (!iconParent) {
                    iconParent = this._iconParent = icon.parentNode
                }
                if (shadow && !shadowParent) {
                    shadowParent = this._shadowParent = shadow.parentNode
                }

                // Add/remove from DOM based on changes
                if (isVisible != wasVisible) {
                    if (isVisible) {
                        iconParent.appendChild(icon)
                        if (shadow) {
                            shadowParent.appendChild(shadow)
                        }
                    } else {
                        iconParent.removeChild(icon)
                        if (shadow) {
                            shadowParent.removeChild(shadow)
                        }
                    }

                    this._wasVisible = isVisible
                }
            }

            // Update visibility of icon in DOM upon map size change
            this._map.on(
                'resize moveend zoomend',
                this._updateIconVisibility,
                this
            )
            this._updateIconVisibility()
        },
        this
    )
})

Answer №2

After listening to David's response, I decided to create an interface that includes elements for the enhanced functionality I needed. Here's how it turned out:

interface LazyMarker extends Marker {
    _wasVisible: boolean
    _icon: ((Node & ParentNode) | null) & Icon
    _iconParent: (Node & ParentNode) | null
    _shadowParent: (Node & ParentNode) | null
    _updateIconVisibility(): void
}

// By using this hook, the markers can lazy load which ultimately boosts the performance of map scrolling and moving.
L.Marker.addInitHook(function (this: LazyMarker) {
    this.on(
        'add',
        function (this: LazyMarker) {
            this._updateIconVisibility = function (this: LazyMarker) {
                var map = this._map,
                    isVisible = map.getBounds().contains(this.getLatLng()),
                    wasVisible = this._wasVisible,
                    icon = this._icon,
                    iconParent = this._iconParent,
                    shadow = this._shadow,
                    shadowParent = this._shadowParent

                // Storing the parent of the icon
                if (!iconParent) {
                    iconParent = this._iconParent = icon.parentNode
                }
                if (shadow && !shadowParent) {
                    shadowParent = this._shadowParent = shadow.parentNode
                }

                // Adding/removing from DOM based on changes
                if (iconParent != null && isVisible != wasVisible) {
                    if (isVisible) {
                        iconParent.appendChild(icon)
                        if (shadowParent != null && shadow) {
                            shadowParent.appendChild(shadow)
                        }
                    } else {
                        iconParent.removeChild(icon)
                        if (shadowParent != null && shadow) {
                            shadowParent.removeChild(shadow)
                        }
                    }

                    this._wasVisible = isVisible
                }
            }

            // On map size change, remove/add icon from/to DOM
            this._map.on(
                'resize moveend zoomend',
                this._updateIconVisibility,
                this
            )
            this._updateIconVisibility()
        },
        this
    )
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transferring information from child to parent class in TypeScript

I have a scenario where I have two classes (Model). Can I access properties defined in the child class from the parent class? Parent Class: class Model{ constructor() { //I need the table name here. which is defined in child. } publ ...

Issue with Nuxt.js generate freezing at 'generated' stage

Just delving into the world of Nuxt App development for the first time and I'm facing a challenge while trying to deploy it on netlify. Whenever I run the command yarn run generate No errors are thrown, but my progress halts at this point: Built at ...

Vue2 Component not displaying changes in data updates

I'm facing an issue where a Vue 2 component fails to render upon updating its data: Vue.component('image-slider', { data: function() { return { name : "Image1", src : "https://via.placeholder.com/250" } }, ...

Issue with Nuxt: Module needs to export a function: @turfhelpers [ERROR]

Can someone explain why I am receiving the error message Module should export a function: @turf/helpers after adding @turf/helpers to my buildModules in nuxt.config.js? nuxt.config.js // Plugins to run before rendering page: https://go.nuxtjs.dev/config-p ...

Retrieve the value of the target element when the Quasar q-checkbox is checked

I'm currently utilizing QUASAR and encountering an issue where I am unable to retrieve the state of my q-checkbox to determine whether it is checked or not. Despite using event.target.checked and event.target.value, both return as undefined. Below is ...

Is there a way to replicate the tree structure of an array of objects into a different one while modifying the copied attributes?

Is there a way to replicate the tree structure of an array of objects to another one in TypeScript, with different or fewer attributes on the cloned version? Here's an example: [ { "name":"root_1", "extradata&qu ...

The data type 'string' cannot be assigned to the data type 'Position'

Currently, I am in the process of converting React js to typescript. The component being used is a Class Component. I would like to obtain CSS settings through props and apply them to an element. How can I resolve this issue? render(){return( <span st ...

Error: The Vue Class-Based module failed to parse due to an unexpected character '@'

When I run the command nmp run serve on my Vue project, I encounter two errors. I am following a tutorial that uses class-based Vue, but I am facing this error with all my imported Vue files. As a newcomer to Vue, I am puzzled as to why this error is occur ...

Choose datetime datepicker formatting in ng-pick

Currently, I am utilizing Angular and have incorporated the ng-pick-datetime npm. However, when attempting to adjust the format after selecting a date (dd/MM/yyyy), it consistently displays as (MM/dd/yyyy) instead. I am uncertain about how to rectify this ...

Using Angular and Typescript to implement a switch case based on specific values

I am attempting to create a switch statement with two values. switch ({'a': val_a,'b': val_b}){ case ({'x','y'}): "some code here" break; } However, this approach is not functioning as expected. ...

Are there alternative methods for anchoring an element in Vuetify aside from using the v-toolbar component?

I have been working on positioning certain elements in the app and I have found a method that seems to work, using code like this: <v-toolbar fixed></v-toolbar> Another option is something along these lines: <v-toolbar app></v-toolb ...

Trouble integrating PDF from REST API with Angular 2 application

What specific modifications are necessary in order for an Angular 2 / 4 application to successfully load a PDF file from a RESTful http call into the web browser? It's important to note that the app being referred to extends http to include a JWT in ...

What is the process for crafting a Vuejs controlled input form?

Although I am familiar with creating controlled components in React using state, I am new to the Vue framework and would like to understand how to adapt my code from React to be compatible with Vue. My goal is to store the values of input fields in a data ...

Creating an interceptor to customize default repository methods in loopback4

Whenever I attempt to access the default repository code, I need to manipulate certain values before triggering the default crud function in the repository. How can I accomplish this? For example: ... @repository.getter('PersonRepository') priva ...

Setting timeouts during Vue-cli unit testing can help improve the efficiency and accuracy of your tests

I have been running vue unit tests using the following command: vue-cli-service test:unit However, I am encountering an issue where some unit tests require a response from the server, causing them to take longer to execute. Is there a way for me to man ...

Is there a method in Vuejs to choose a tab and update components simultaneously?

Currently facing an issue where selecting a tab does not refresh the input field in another component, causing data persistence. The data is stored in vuex, so I'm looking for a solution to refresh the component for usability. Appreciate any assistanc ...

The parent component is failing to pass the form values to the child form group in CVA

My Angular application (view source code on Stackblitz) is running Angular 15, and it utilizes reactive forms along with a ControlValueAccessor pattern to construct a parent form containing child form groups. However, I am encountering an issue where the d ...

My previously functioning TypeScript code suddenly ceased to work after I ran the yarn install command

Everything was running smoothly with my TypeScript code, both locally and on the server. However, after restarting the production code, I encountered errors (which required me to reinstall packages with yarn install). Strangely enough, when I try to yarn i ...

Is `h` equal to `createVNode` function?

Both h and createVNode are exposed from vue. The documentation on this page seems to imply that they are interchangeable: The h() function is a utility to create VNodes. It could perhaps more accurately be named createVNode(). However, replacing h with ...

How can we combine two phone calls and display the outcomes using typeahead ngx-bootstrap?

Let me walk you through the code that is currently working: <input [formControl]="search" [typeahead]="suggestions" typeaheadOptionField="name" (typeaheadOnSelect)="onSelectedDriver($event)&qu ...