0

I'm working in a Typescript Vue project with Leaflet. I've found some code to lazy-load map markers, but it was written in Javascript. It works fine, but I get VSCode errors and compiler warnings because this is not typed. How do I type this?

Update 1: I got the first type to resolve using @aplet123 suggestion below. But I can't figure out the second one on this._updateIconVisibility. I think this is because I'm actually using the function to extend the existing functionality (i.e. _updateIconVisibility does not actually exist on a type). So now what do I do? I suspect it is best practice to just create a custom class with the methods I need, but I don't know if it is actually more common to provide maybe an anonymous object or something else...

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
    )
})

VSCode error

4
  • Give the function a typed argument called this (it's not an actual argument and won't be compiled into the resulting javascript, it's just to notate the this type). Commented Dec 14, 2020 at 20:36
  • it has to do with your project configuration vuejs.org/v2/guide/typescript.html update your tsconfig.json and set strict: true Commented Dec 14, 2020 at 20:38
  • @Aplet123 Thanks that works for the first instance. I've updated my question. Additionally, if I run into a circumstance later where the function had required arguments, would this still work? would I just pass those arguments after the this argument and they will be in the correct order? Commented Dec 14, 2020 at 20:58
  • @BooklynDadCore Thanks, I already have set strict: true in my .tsconfig Commented Dec 14, 2020 at 20:59

2 Answers 2

2

I'm not ultra-familiar with your environment (Vue, etc.) but I was able to construct the following file that eliminates the errors and hopefully demonstrates the way forward. It could likely be improved if I knew more.

The key is really describing your this types at each step. So I created a bit of scaffolding, created a type MyObject to describe the decorations you're implementing, and then declared this to be of type MyObject at the appropriate spot.

See below, and let me know if there's stuff that's not clear.

//  Scaffolding to account for things from the environment
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) {
            //  just here because your code must be supplying it
        }
    }
}

//  A type to add to your code
namespace L {
    export type MyObject = {
        _updateIconVisibility: () => void,
        _map: {
            on: on
        }
    }
}

//  Your code above, now with no objection from the TypeScript compiler
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

                // 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
    )
})
Sign up to request clarification or add additional context in comments.

Comments

0

David's answer led me to just define an interface which contains members for the extended functionality. It ends up looking like this:

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

// this hook makes the markers lazy load
// increases performance of map scrolling/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

                // 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 (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
    )
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.