In an Error, the property stack is optional. However, when I throw a new Error('myError') or when I have a generic error thrown during the execution of my script, I always get the stack. According to the browser, the stack is not the same but I is not undefined.
I was wondering in which case it was possible to have an undefined stack?
The stack property is not defined by the JavaScript specification (yet). So a compliant JavaScript implementation could well not have a stack property on Error instances. Most do nowadays (certainly the big three: V8, SpiderMonkey, and JavaSciptCore), but it's not standardized (yet), so they don't have to.
In an Error, the property stack is optional.
I'm guessing you're referring to TypeScript's type information for Error:
interface Error {
stack?: string | undefined;
}
Since most JavaScript engines do provide a stack property of type string even though it's not defined by the specification, making it optional with the type string | undefined is a pragmatic choice (the TypeScript project team are notoriously pragmatic). So rather than leave it out entirely, they acknowledge the common practice.
I was wondering in which case it was possible to have an undefined stack?
I think it's unlikely you'd have an undefined stack on an Error instance on implementations that support it (more likely "" if no stack information is available, but I don't recall seeing a blank stack). That said, there's likely a reason TYpeScript's types define it as stack?: string | undefined and not stack?: string.
Related
You can change the global variable of undefined to a value and you can read a property from a string, but you can't read the property of undefined.
The first two (the ones you can do) seem completely useless and the third is something I think most developers would love to work. If you make an API call and you're trying to handle complex data, why not just evaluate undefined.undefined.undefined.undefined = undefined instead of literally crashing the program? I know about optional chaining but I'm curious why is this even an issue. It seems like an easy fix but I'm guessing there is something idk about.
The point of errors in programming is that they inform you of a serious contract violation or illegal state. You expected something to be defined, but it wasn't. You expected something to be a certain type but it wasn't. Usually, the program shouldn't try to continue under a contract violation, and if it should, you'd want to make that explicit and at least log the violation rather than suppress it completely with head buried in the sand.
In many languages, these sort of invariant violations and illegal operations cause explicit errors, either at runtime or in compilation. The earlier you can catch the problem (thanks to a compiler error, warning or runtime crash/error during testing), the sooner and easier you can fix the bug.
JS has a reputation as stubborn to raise errors due to weak typing and other permissive design choices. You can concatenate and perform math on pretty much any types, or compare values with coercive equality operator == and wind up with anyone's-guess results. The reason is mostly historical: JS was designed for quick, simple scripts, not for massive single-page applications of today.
The reason technology like TypeScript exists is to essentially add errors to JavaScript to make programs safer and support the sort of large, industrial-grade applications that were never envisioned in 1995 when JS was designed. These errors are purely compile-time in the case of TS, but other languages like Python won't let you do many of the things JS does at runtime, like adding two different typed values "abc" + 42 which raises a TypeError (dictionary [a sort of Map] accesses also raise an error; you have to opt-in to the default value equivalent of undefined).
Simarly, JS' strict mode "eliminates some JavaScript silent errors by changing them to throw errors".
The contrary philosophy is to suppress errors, which is exactly what the optional chaining operator ?. offers, and it sounds like you're suggesting the default object property operator . should as well. This is syntactical sugar for code that returns undefined when an object doesn't exist, and it's nice to use occasionally to keep code terse and communicate intent, saying essentially "look, I realize this particular access might be on an undefined object, and my code is designed such that it expects that case and will continue on with the expression evaluating to undefined".
But it's a good thing this isn't the default behavior: it's essentially suppressing an error, pretty much the same rationale behind "abc" * 42 returning the default value NaN instead of throwing a type error in JS. Why would you ever want this? Probably never, hence JS's reputation as unsafe.
As I mentioned in the comments, React also made changes to ascribe to the "fail fast, fail loudly, fail hard" philosophy:
[...] in our experience it is worse to leave corrupted UI in place than to completely remove it. For example, in a product like Messenger leaving the broken UI visible could lead to somebody sending a message to the wrong person. Similarly, it is worse for a payments app to display a wrong amount than to render nothing.
[...] This change means that as you migrate to React 16, you will likely uncover existing crashes in your application that have been unnoticed before.
[...] We also encourage you to use JS error reporting services (or build your own) so that you can learn about unhandled exceptions as they happen in production, and fix them.
Here's advice from an article on javascript.info:
Don't overuse the optional chaining
We should use ?. only where it's ok that something doesn't exist.
For example, if according to our coding logic user object must exist, but address is optional, then we should write user.address?.street, but not user?.address?.street.
So, if user happens to be undefined due to a mistake, we’ll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
And here's a nice blog post on the matter
I'm loading a few environment variables into my Node & Express app using dotenv and a lot of these values are critical to the app's function so before doing anything I validate these variables to make sure they're included and that they have the correct type, otherwise I throw an error and stop the server from starting.
I now need to use these variables down the line, but TypeScript does not recognize that I have narrowed the types so it prevents me from doing things like process.env.test.includes (without optional chaining or nearby type assertion) saying Object is possibly null or undefined (I have strict mode enabled). I know in some cases it'll recognize that I have narrowed the type if I do type checks but I'm guessing this is not nearby enough to the code for it to pick up on this. Is there any way to signal that I have already narrowed the type from string | null | undefined to just string, for example, or do I have to continue to use the optional chaining operator?
You can use the non-null assertion operator ! to tell TypeScript that you know better, that it should never be null/undefined:
process!.env!.test!.includes
This is of course assuming that any of the keys in the chain could return null/undefined, but you probably don't need to use the operator on every single accessor.
const myError = new Error("bug");
myError.propertyIsEnumerable("message") //false
myError.propertyIsEnumerable("stack") //false
The implications of this are that when you for loop this kind of object, you won't fetch these fields, which I find strange.
What was the reasoning behind that?
The reason that I can think of is to maintain the behaviour of Object.keys. If you see the initial specs, the error part not very detailed. the error specs are
This specification specifies the last possible moment an error occurs. A given implementation may generate errors
sooner (e.g. at compile-time). Doing so may cause differences in behavior among implementations. Notably, if runtime
errors become catchable in future versions, a given error would not be catchable if an implementation generates the
error at compile-time rather than runtime.
An ECMAScript compiler should detect errors at compile time in all code presented to it, even code that detailed
analysis might prove to be “dead” (never executed). A programmer should not rely on the trick of placing code within
an if (false) statement, for example, to try to suppress compile-time error detection.
In general, if a compiler can prove that a construct cannot execute without error under any circumstances, then it may
issue a compile-time error even though the construct might neverbe executed at all.
You can see the first specs at https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%201st%20edition,%20June%201997.pdf
The Nashorn Javascript engine (part of Java 8) reports some exceptions, esp. regarding method binding errors like arity or type mismatches, as a NativeTypeError-instance, which does not have a cause relation to get any exception and is therefore very opaque to me (I'd like to report as much as possible back to the script user).
Is there a documented method to get root causes or further information from a NativeTypeError-instance?
ECMAScript standard only defines name and message properties on Error objects like TypeError, ReferenceError etc.
Nashorn defines certain extension properties such as "lineNumber", "columnNumber", "fileName" (for the source where exception was thrown from) and "stack" to access stack trace. Also "printStackTrace", "getStackTrace" functions are supported via Error.prototype extensions.
See also:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-ExtensionsofErrorobjects,Error.prototypeandErrorconstructor
Answering my own question: the semantics of NativeTypeError are described in the ECMAScript standard (ECMA-262, sec. 15.11.7). Instances of NativeTypeError expose a public field message, which is of type Object, but is most likely a String for all circumstances I've encountered it.
What type of object should be thrown in JavaScript?
I see a lot of examples which throw a plain old string and there seems to be a semi-standard Error type. Should I prefer one over the other?
The Error object and specific error objects such as TypeError are fully standardized in the ECMAScript specification. There are, however, common non-standard properties of these objects available in most browsers.
You can throw whatever you like, so long as your error handling code knows what to do with the objects you throw, but there are advantages to using Error objects:
Consistency with handling errors thrown by native code, such as having a message property, so you don't have to write different code to handle native errors and your own errors;
Error objects in Mozilla and other browsers have very useful non-standard properties, such as fileName, lineNumber and stack. You only get these on Error objects and they can be very useful for debugging.