Search code examples
javascripttypescripttypeserror-handlingcross-browser

What's the proper way of using a browser specific property in typescript?


Specifically, I'd like to use the "fileName" and other extra properties on the Error object in FireFox.

But something like this gives me red underlining, which I could simply ignore, but it bothers me. I tried

function normalizeError(e:Error|ErrorEvent|PromiseRejectionEvent|any) {
    if(e instanceof Error){
        return {
            line: e?.fileName //underlined
        }
    }else return null
}

This is underlined because fileName is a property only found in a subset of browsers and it is not stanardized, but it is on the Error object which I have specified in the argument type.

Modifying the interface with declaration merging works sort of...

interface Error {
    fileName?: string
}
function normalizeError(e:Error|ErrorEvent|PromiseRejectionEvent|any) {
    if(e instanceof Error){
        return {
            line: e?.fileName // Not-underlined
        }
    }else return null
}

But.. when I export the function, it underlines it again?... Why?

interface Error {
    fileName?: string
}
// Exported now:
export function normalizeError(e:Error|ErrorEvent|PromiseRejectionEvent|any) {
    if(e instanceof Error){
        return {
            line: e?.fileName // underlined again
        }
    }else return null
}

An image to show the underline: typescript underlining missing property

So why doesn't declaration merging work anymore when I export the function? What can I do to remove the underlining?

Addendum 1:

I noticed that if I reassign e to itself, there is no longer an error... What? See: typescript code assigning error to itself

Apparently assigning e to e changes it from an Error type to an "any" type, which gets rid of the property does not exist error. I would have assumed that the type wouldn't change when essentially assigning it to itself, but I guess I assumed wrong.

Addendum 2:

I think I'm just going to use // @ts-ignore until someone clarifies the proper way of clarifying to Typescript that the properties might actually be available.


Solution

  • As I mentioned in the comment, an interesting approach as shown in this link is used to make the type-checker happy in a case like this: an abstract class extending the class of your object.

    declare abstract class Error extends globalThis.Error {
        public fileName: string
        // (...)
    }
    

    I consider this a better option than overwriting your type declaration globally for two reasons:

    • It doesn't mess with the global namespace; you can restrict it tho the scope of your current module
    • Your new class derives from the original class, so type-checking works wonders and you can return or pass this object around and it will also work due to polymorphism.