2

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.

3
  • Just curious, can anyone explain why e=e changes the type of e?
    – ADJenks
    Commented May 15 at 23:59
  • Take a look into this answer and this one also. Commented May 16 at 1:34
  • @RodrigoRodrigues The second link seemed useful. I've added a second implementation to my answer based on that resource. Feel free to write your own answer to get the bounty.
    – ADJenks
    Commented May 16 at 4:11

2 Answers 2

1
+50

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.
0

Apparently I needed to declare them globally like so:

// global.d.ts

export {} // Exporting something is required

declare global {

    interface Error {
        /** Mozilla specific */
        lineNumber?:number,
        /** Mozilla specific */
        columnNumber?: number,
        /** Mozilla specific */
        fileName?: string,
    }
}

This method can be found here: https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html

This seems like the most proper way to me.

Is there an official library with these types? I could not find one.

@Rodrigo Rodrigues enlightened me to another option, declaring an abstract class that extends the error class. I'm not sure which option is better. It's probably better to not modify the base type declarations globally, so this method might be better. I tested adding this in the same file and it seemed to work:

declare abstract class Error extends globalThis.Error {
    /** Mozilla specific */
    public lineNumber: number
    /** Mozilla specific */
    public columnNumber: number
    /** Mozilla specific */
    public fileName: string
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.