Angular Technology Stack

(Based on Angular 10)

0. Angular-CLI

Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell.

It is the MUST have tool to write Angular code, so make sure you are familiar with its commands.

1. IDE

1.1 Visual Studio Code

Visual Studio Code: Best and free IDE for web developing.
Plugins:

  • Angular Language Service - This extension provides a rich editing experience for Angular templates, both inline and external templates.
    This extension is brought to you by members of the Angular team. It is fantastic at helping write solid code in the html templates.
  • Prettier - VS Code plugin for prettier/prettier, which formats code consistently. Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.
  • Code Spell Checker - A basic spell checker that works well with camelCase code.
    The goal of this spell checker is to help catch common spelling errors while keeping the number of false positives low.
  • GitLens - If you use git, this is quite helpful. GitLens supercharges the Git capabilities built into Visual Studio Code. It helps you to visualize code authorship at a glance via Git blame annotations and code lens, seamlessly navigate and explore Git repositories, gain valuable insights via powerful comparison commands, and so much more.

1.2 Alternatives

  • Atom by GitHub
  • Sublime Text 3
  • PyCharm
  • IntelliJ IDEA
  • Webstorm

There are may alternatives, you can choose which you are familiar with.
*

2. UI Framework

2.1 Angular Material

Angular Material is Material Design components for Angular.
There are many alternatives. But Angular Material is the official one from Angular, and Angular also give detail upgrade solution on Angular plus Angular Material in https://update.angular.io/.

For future upgrade reason, this is your best choice.

3. State Management - NgRx

@ngrx/store is RxJS powered global state management for Angular applications, inspired by Redux. Store is a controlled state container designed to help write performant, consistent applications on top of Angular.

NgRx Store is mainly for managing global state across an entire application.

4. Code Reuse

4.1 Angular libraries

Angular libraries should what you need.

Many applications need to solve the same general problems, such as presenting a unified user interface, presenting data, and allowing data entry. Developers can create general solutions for particular domains that can be adapted for re-use in different apps. Such a solution can be built as Angular libraries and these libraries can be published and shared as npm packages.

An Angular library is an Angular project that differs from an app in that it cannot run on its own. A library must be imported and used in an app.

You can easily reuse your Angular library or also submit your library into npm server.

4.2 Alternatives - Angular custom elements

Note that libraries are intended to be used by Angular apps. To add Angular functionality to non-Angular web apps, you can use Angular custom elements.

Angular elements are Angular components packaged as custom elements (also called Web Components), a web standard for defining new HTML elements in a framework-agnostic way.

Angular Unit Test - Cannot find module “tslib”

Angular Unit Test - Cannot find module “tslib”

When running angular UT, you may encounter the error message looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Uncaught Error: Cannot find module 'tslib'
at webpackEmptyContext (src sync:2) [<root>]
at :9876/_karma_webpack_/d:/workspace/web/node_modules/@angular/compiler/src/core.js:19:1 [<root>]
at :9876/_karma_webpack_/d:/workspace/web/node_modules/@angular/compiler/src/core.js:10:1 [<root>]
at Object.../../node_modules/@angular/compiler/src/core.js
(:9876/_karma_webpack_/d:/workspace/web/node_modules/@angular/compiler/src/core.js:16) [<root>]
at __webpack_require__ (bootstrap:79) [<root>]
at Module../src/lib/service/service.spec.ts (xx.service.ts:36) [<root>]
at __webpack_require__ (bootstrap:79) [<root>]
at webpackContext (src sync \.spec\.ts$:22) [<root>]
at Array.map (<anonymous>) [<root>]
at Module../src/test.ts (test.ts:23) [<root>]
at __webpack_require__ (bootstrap:79) [<root>]
at checkDeferredModules (bootstrap:45) [<root>]
at :9876/_karma_webpack_/webpack/bootstrap:152:1 [<root>]
at :9876/_karma_webpack_/main.js:156:10 [<root>]

This should be coming from Angular Core

removed:

1
import { Type } from '@angular/compiler/src/core';

Using:

1
import { Injectable, Type } from '@angular/core';

Angular State Management - ngrx

@ngrx/store

Store is RxJS powered global state management for Angular applications, inspired by Redux. Store is a controlled state container designed to help write performant, consistent applications on top of Angular.

The following diagram represents the overall general flow of application state in NgRx.

Key concepts

  • Actions describe unique events that are dispatched from components and services.
  • State changes are handled by pure functions called reducers that take the current state and the latest action to compute a new state.
  • Selectors are pure functions used to select, derive and compose pieces of state.
  • State is accessed with the Store, an observable of state and an observer of actions.

SonarQube Tips

Make sure this cross-domain message is being sent to the intended domain

HTML5 adds the ability to send messages to documents served from other domains. According to the specification:

Authors should not use the wildcard keyword ( *) in the targetOrigin argument in messages that contain any confidential information, as otherwise there is no way to guarantee that the message is only delivered to the recipient to which it was intended.
To mitigate the risk of sending sensitive information to a document served from a hostile or unknown domain, this rule raises an issue each time Window.postMessage is used.

Noncompliant Code Example

1
2
var myWindow = document.getElementById('myIFrame').contentWindow;
myWindow.postMessage(message, "*"); // Noncompliant; how do you know what you loaded in 'myIFrame' is still there?

Solution

This rule detects only if a method postMessage is invoked on an object with a name containing window in it. Source code: PostMessageCheck.java. To bypass it, just assign your contentWindow object into different one, like this:

1
2
var content = this.elem.contentWindow;
content.postMessage('your message', window.location.origin);

Using Angular Library

Overview - Angular Library

Using Angular Library
Environment: angular v8.2; node.js v12

Create

You can create an Angular library within an existing application. e.g. lib name is my-lib. Commands:

1
ng generate library my-lib`

Build

1
ng build my-lib

Tips

Angular lvy compatibility

Ivy applications can be built with libraries that were created with the View Engine compiler. This compatibility is provided by a tool known as the Angular compatibility compiler (ngcc). CLI commands run ngcc as needed when performing an Angular build.

In version 9, the server builder which is used for App shell and Angular Universal has the bundleDependencies option enabled by default. If you opt-out of bundling dependencies you will need to run the standalone Angular compatibility compiler (ngcc). This is needed because otherwise Node will be unable to resolve the Ivy version of the packages.

You can run ngcc after each installation of node_modules by adding a postinstall npm script

1
2
3
4
5
6
// package.json
{
"scripts": {
"postinstall": "ngcc"
}
}

The postinstall script will run on every installation of node_modules, including those performed by ng update and ng add.
Don’t use –create-ivy-entry-points as this will cause Node not to resolve the Ivy version of the packages correctly.

How to Promote M4rix offers with traffic

M4Trix

M4trix offers a plethora of private offers, ranging from health and beauty to E-commerce, covering the full spectrum of payout models. We control the entire flow from supply to local delivery and customer support, allowing us to deliver unmatched performance for your traffic.

  • Selection of offers is available worldwide. We specialize in exclusive “never seen before” in the industry geos, with an emphasis on hyper-profitable countries such as the EMENA area. Our affiliates experience instant profit testing in these non-saturated markets.
  • Smart Link Technology dynamically matches each click with the most enhanced intelligence to maximize your performance. Languages, currencies, creatives, processors and cross platform performance are optimized in near real-time.
  • Focus on the personal relationships with each of our affiliates, providing a full suite of resources and a close one on one relationship to make you profitable. Localized advertorials flow, market analysis and creatives are standard assets we provide for each campaign. Already running high volume? Take the red pill and double your EPC.

Tracking M4trix Offers

If using a different tracker, simply copy and paste this onto the end of your Offer’s URL.

1
cid={cid}

Postback url, appends

1
cid={cid}&payout={payout}

From M4Trix

A Private Affiliate Network opening up our own offers.

Everything in the M4TRIX is 100% owned and controlled by us, which allows to offer higher performance and payouts for our Affiliates.

Every network will claim the “Higher EPC” and “Higher Payout” thing, realistically speaking, the more intermediary there is, the more people need to get paid and the less there is left for the affiliate at the end of the chain. When dealing with the M4TRIX Network, you are talking directly with the advertiser, you are the only one in the chain!

We own and control multiple offers ranging from High Scale Worldwide E-Commerce to Health & Beauty in Exclusive Geos.

Inside the M4TRIX you’ll get the choice between the classic Pay Per Sale model or our Cash On Delivery offers (Pay Per Lead) in Private Geos such as North Africa and the Middle East. We are the only advertiser in the space, these offers and geos can not be found anywhere else.

The M4TRIX aim to revolutionize the industry with a more transparent relationship and access to new untapped markets. We focus on the personal relationship, we provide localized Pre-Sell and resources to get you started and profitable.

Our payment terms are Weekly Net4. Faster payouts are available for Hyper Affiliate generating $100.000+ per day.

Register Now and Experience the M4TRIX!

How to use Affiliate Pixels and Tracking in Getcake

Affiliate Pixels and Tracking

This article will cover the following:

  • Affiliate Pixels

  • Tokens in Affiiate Pixels

  • Restricting Affiliate Pixels

Affiliate Pixels
Affiliates pixels are placed at the campaign level and can be placed directly by the affiliate through the Affiliate Portal. Alternatively, the pixel can be placed by an admin employee in the Admin Portal as well.

Placing pixels in the Admin Portal
Zoom:

In the Admin portal you can place an affiliate pixel on the Campaign card > Home tab > Pixels sub-tab.

You can view a list of tokens by using the hover menu for the Setup tab > Show Tokens. The full list of available tokens includes the following:

1
2
3
4
5
6
7
8
9
10
11
Affiliate ID: #affid#
Offer ID: #oid#
Campaign ID: #campid#
Creative ID: #cid#
Sub ID 1: #s1#
Sub ID 2: #s2#
Sub ID 3: #s3#
Sub ID 4: #s4#
Sub ID 5: #s5#
Request ID: #reqid#
UDID: #udid#

Conversion Pixels Only:

Transaction ID: #tid#
Lead ID: #leadid#
Price (Affiliate Payout): #price#
Click IP: #sourceip#
Conversion IP: #ip#
Conversion UNIX timestamp: #utcunixtime#
Conversion timestamp: #sourcedate# (ie. 2013-11-12T13:04:27.5670000)

Placing pixels in the Affiliate Portal
Zoom:
Affiliate pixels are placed in the Offer card > Testing & Tracking tab. Here, your Affiliate will enter their test link and their pixel. A list of tokens is available on the left-hand side, and clicking on them will place the corresponding token wherever your cursor is located.

Once placed, CAKE will display these pixels in the Affiliate tab > Pending > Pixels section.

There is an optional alert associated with Affiliate pixel placements - the ‘Pixel Was Placed on Campaign’ to alert you that an affiliate has placed a pixel. Such an alert allows you to verify the proper formatting or the type of pixel once it is placed to help proactively avoid tracking errors. For example, if your advertiser places your postback or server pixel, your affiliate needs to use a postback as well. For this reason, we have a setting to restrict the type of pixel your affiliate places.

Restricting Affiliate Pixels
In order to prevent the chance that an affiliate will place a web-based pixel (iframe / javascript / image) in error, CAKE has created a setting to restrict affiliates to placing postbacks only. You can enable this across the board for every affiliate and campaign in Setup > Settings > Affiliate Portal Settings > Affiliate Permissions. The specific setting is the first in the section to ‘Only Allow Affiliate Postbacks’.

With no further action, the Affiliate will no longer be able to place any pixel other than a postback through the Affiliate Portal. Every campaign will now have a warning icon stating that this setting has been enabled.

NOTE: If you have a postback placed with your Advertiser, placing a non-postback even in the admin will not function.

If you want to pick and choose where and when to apply this feature, you can do that as well. Without the global setting, every offer will include a option (which goes away with the Global Setting enabled) for “Postbacks Only”. Enabling this checkbox on the offer card will immediately pop up the warning icons on the campaigns for that offer.

Tracking Settings

Section 1: Where to access Tracking Settings

To access your Tracking Settings click on the main Setup tab > Settings sub tab > Tracking Settings

Section 2: General Settings

Cookie Domain: The cookie domain is used for either affiliate tracking links, conversion pixels or as a POST URL for server posting into CAKE. This domain is decided upon during the onboarding process.

Session Regeneration Minutes: Duplicate clicks/conversions will not be counted if they occur within the specified time period. Users may override at the offer level.

Global Redirect: The default URL where a customer is redirect on invalid/inactive links, after all Upsell and Redirect paths have been exhausted and when no Thank You Link is present.

Paid Redirects: If selected, campaign redirects will be paid. Users may override at the affiliate level on the Affiliate card.

Encrypt Links: If enabled, all generated unique links and click/impression pixels will be encrypted to hide identifying source information.

Base Tracking Domain Redirect: The URL where a customer is redirected on clicks to a base tracking domain sans affiliate/query string info. If left blank, the Global Redirect will be used.

Don’t pay for Bot Traffic on CPC: If enabled, conversions will not be recorded from bot traffic on all CPC campaigns. Clicks will be recorded but without payment. If disabled, conversions will be recorded from my traffic on all CPC campaigns. Clicks will be recorded and paid for.

Don’t pay for Bot Traffic on CPM: If enabled, conversions will not be recorded from bot traffic on CPM campaigns. Impressions will still be recorded sans payment.

Enable Events: Enable this option to add event tracking options.

Grouped Offers: If enabled, this will turn on Grouped Offers (1:Many tracking)

Child Click Inherits Parent SubIDs: When enabled the values of s2-5 on the child offer’s click will be inherited from the parent offer’s click (note: sub ids from the child offer will be ignored). When disabled, only s1 will be inherited and s2-5 will be taken from the child offer’s click.

Cap Percentage Threshold: This allows CAKE users to override the default cap threshold of 90%, to a different percentage. Example: setting this to 50% will trip the alert when 50% of the cap is reached.

Section 3: Global Pixels & Postbacks

Global Pixels: Enables pixel placement at the affiliate, offer and global level.

Global Pixel: Pixels placed here will fire on all offers unless otherwise modified at the offer or affiliate level.

Postback URL: This URL will fire on all offers unless otherwise modified at the offer or affiliate level.

Postback Delay (ms): The delay (in milliseconds) before firing the global postback upon any conversion.

Fire Global Pixel By Default: If enabled, all new offers will fire global pixel by default.

Fire Pixel on Non Paid Conversions: If enabled, the global pixel fires on non paid conversions.

Section 4: Conversions

Pending Conversions: If enabled, all conversions will be sent to Queue.

Default Approved: Sets the default disposition for auto approved conversions.

Default Pending: Sets the default pending disposition for all conversions set for review at the campaign or affiliate level.

Default Rejected: Sets the default disposition for auto rejected conversions.

Conversion Cap Behavior: This setting allows you to customize how incoming conversions are dealt with once a conversion cap is hit. Any new or existing offers not assigned an explicit conversion cap behavior will be defaulted to this conversion cap behavior.

Conversion Behavior On Redirect: This setting allows you to customize how incoming conversions are dealt with when any redirect criteria is met (e.g. Inactive Affiliate, Expired offer), expect for conversion caps whose default behavior can be individually customized above. Any new or existing offers will be defaulted to this conversion behavior on redirect.

Section 5: Original Campaign Settings

Original Campaign by Offer Contract: Account for offer contract in original/non-original campaign creation.

Original Campaign by Media Type: Account for media type in original/non-original campaign creation.

Top 10 Affiliate Marketing Programs For 2020

StudioPress Affiliate Website

StudioPress is another rather a niche but very worthwhile affiliate to pursue if you have the digital presence audience. They create responsive, adaptable and highly customisable WordPress hosting and themes, that extend the function and accessibility of WordPress fluidly.

Studiopress Affiliate Marketing Website

Pros

Another product that sells itself. Most WordPress users take one look at StudioPress’ work and fall in love with what they can achieve with it. Which makes your job easy.
Generous payouts, that grow the more you refer (to a point). 35% per theme sale and a minimum of $75 per site sale made can add up if your audience is interested.
Cons

Still a niche product. It requires an audience that wants to establish themselves online. No matter what the niche, that’s a demand that’s growing. But it’s still not for everyone… Yet.

CJ Affiliate Publisher’s Program

CJ Affiliate is a platform that knows what they’re talking about. They’ve been in the Affiliate Marketing industry since 1998 and that expertise just shines through in every aspect of what they do. With products in every niche, you’d be hard-pressed to find fault with them.

CJ Affiliate Platform

Pros

One of the largest Affiliate Networks. When you’ve been in business as long as CJ Affiliate has, it’s hard for other programs to rival them in size.
A network you can rely on. 19 years in business doesn’t make them infallible, but it does show they can expertly adapt. You can rely on them to continue to do so.
Cons

There’s a pretty scrutinous application process. You don’t get to succeed for almost 20 years without high standards, so this is to be expected.

11. ConvertKit Affiliate Program

ConvertKit is a leading email marketing platform. With ideology based around being customisable and uniquely fitting to every user, you can understand why they come highly recommended by many top names in the email marketing industry.

ConvertKit Affiliate Program

Pros

If your audience uses email marketing, they will love ConvertKit. It’s just a fact – if you’re in that space, ConvertKit easily reaches every need of it that you can conceive.
No cut-off of the affiliate revenue. As long as your affiliate’s account is active and paying – you’re getting paid. There’s absolutely no point where they’ll cut you off.
Cons

Not everyone uses email marketing. That’s just the reality. Some of your audiences will not be using it, so this isn’t a great affiliate for you.
Every commission takes 30 days to confirm. As they have a 30-day, no questions asked, refund policy – they can’t credit you with the commission until 30 days have passed.

1. Fiverr Affiliate Program

Fiverr Affiliates Commission Structure
Fiverr Affiliates: Commission Structure
Promote the world’s largest marketplace for digital services - Fiverr. From whiteboard explainer videos to Wordpress and logo design, earn up to $150 for every first-time buyer with their dynamic CPA model. Get paid to drive traffic, it’s that easy.

With over 3 million digital services available, 250 unique categories and a gig purchased every 4-seconds, anyone can easily find a relevant service to promote from Fiverr’s marketplace.

Start earning with Fiverr - Join Now ⟶

Maximize your affiliate potential with Fiverr’s new stores for affiliates. As an affiliate, there are some things you do the best. You might be the best content writer, the best marketer, or the best videographer in your industry, but… there are some tasks you clearly struggle with. You may need help developing creative copy for Facebook posts or crafting that unique thumbnail image to increase clicks on Youtube, the list goes on and on.

To save you time and increase your affiliate success, Fiverr has created the ultimate set of stores to support your affiliate efforts with premier talent. Those stores include the most relevant and best performing gigs from their top sellers in variety of categories: Web or Blog, YouTube, Facebook and Instagram.

ClickFunnels Affiliate Program

Want to have your dream car paid for? Simply refer 100 customers to ClickFunnels and your new car could arrive sooner than you think.

“Get Just 100 New ClickFunnels Users…And We’ll [ClickFunnels] Cover The Payments On YOUR Dream Car!”

ClickFunnels Affiliates Dream Car Promotion
As an affiliate you also have the ability to earn huge recurring commissions on all of their top level products and services. The ClickFunnels Affiliate Program offers a generous 40% recurring commissions on all sales you send their way. On a starter plan that equates to $38.80 every month (MRR*), per customer, for life!

Wealthy Affiliate Program

Wealthy Affiliate
Wealthy Affiliate Community Dashboard
Turn your passion into a thriving online business with Wealthy Affiliates. Simply follow their proven process easy to use tools to launch a website, attract visitors, and most importantly, earn revenue. Wealthy Affiliate was created by Kyle and Carson, who in 2005, launched the Wealthy Affiliate platform to help other people succeed online. Since then Wealthy Affiliate community has grown to over 800,000 internet entrepreneurs.

Launch your online business, then make money sharing your secret with others. Wealthy Affiliate has one of the most engaging affiliate programs available online — earn consistent revenue by simply referring others to the community. Average commission $100 per premium signup.

Media.net

Media.net ambassador program
Tap into one of the largest pools of advertisers in the world and let Media.net ads maximize your monetization. Monetize every single impression by seamlessly with Contextual ads, Display ads, and customizable Native ads- no additional coding required. Plus, join their ambassador program to earn 10% commission on all referral earnings for the first 12 months.

Leadpages

Leadpages affiliate program
Join the Leadpages Affiliate Partner Program a start promoting their suite of lead generation tools, which have helped hundreds of thousands of entrepreneurs create sustainable small businesses online. Earn 30% recurring commission on all sales you generate, for as long as your referrals remain customers, including renewals and upgrades of their Leadpages plan.

Shopify Affiliate Program

Shopify
Earn 200% commission based on the price of the subscription your referral signs up to. Shopify is one of the most well-known eCommerce platforms, website builders and point of sales software providers in the the world. Earn an average of $58 for each paid signup who uses your unique referral link, and $2000 for each Plus referral.

Vue or Angular in 2020

You should first make a clear decision as to whether you need a single-page-application (SPA) or if you’d rather take a multi-page approach.

Angular

Angular is a TypeScript-based Javascript framework. Developed and maintained by Google, it’s described as a “Superheroic JavaScript MVW Framework”. Angular (also “Angular 2+”, “Angular 2” or “ng2”) is the rewritten, mostly incompatible successor to AngularJS (also “Angular.js” or “AngularJS 1.x”). While AngularJS (the old one) was initially released in October 2010, it is still getting bug-fixes, etc. — the new Angular (sans JS) was introduced in September 2016 as version 2.

Vue

The greatest benefit of Vue is it’s absence of pedigree. It is fresh and has little baggage. It has been learning from the mistakes and successes of React & Angular. The way we see it, Vue is lightweight & easy to learn.
It has got some fairly basic docs but they do a good job, and while there isn’t a great deal to learn compared to angular – this is a good thing as it’s deceptively powerful. PageKit, Python China are two of of the projects using Vue among many. Here’s the list. Also, it has two way data binding facility like Angular and Virtual DOM like React.

Compare

Long-term support & migrations

Regarding Angular, there is a blog post about versioning and releasing Angular starting with the v2 release. There will be one major update every six months, and there will be a deprecation period of at least six months (two major releases). There are some experimental APIs marked in the documentation with shorter deprecation periods. There is no official announcement yet, but according to this article, the Angular team has announced long-term-support versions starting with Angular 4. Those will be supported for at least one year beyond the next major version release. This means Angular 4 will be supported until at least September 2018 with bug-fixes and important patches. In most cases, updating Angular from v2 to v4 is as easy as updating the Angular dependencies. Angular also offers a guide with information as to whether further changes are needed.

The update process for Vue 1.x to 2.0 should be easy for a small app — the developer team has asserted that 90% of the APIs stayed the same. There is a nice upgrade-diagnostic migration-helper tool working on the console. One developer noted that the update from v1 to v2 was still no fun in a big app. Unfortunately, there is no clear (public) roadmap about the next major version or information on plans for LTS versions.

One more thing: Angular is a full framework and offers a lot of things bundled together. React is more flexible than Angular, and you will probably wind up using more independent, unsettled, fast-moving libraries — this means that you need to take care of the corresponding updates and migrations on your own. It could also be a detriment if certain packages are no longer maintained or some other package becomes the de facto standard at some point.

Javascript Error Handling

Error handling, “try..catch”

No matter how great we are at programming, sometimes our scripts have errors. They may occur because of our mistakes, an unexpected user input, an erroneous server response, and for a thousand other reasons.

Usually, a script “dies” (immediately stops) in case of an error, printing it to console.

But there’s a syntax construct try..catch that allows to “catch” errors and, instead of dying, do something more reasonable.

The “try..catch” syntax

The try..catch construct has two main blocks: try, and then catch:

1
2
3
4
5
6
7
8
9
try {

// code...

} catch (err) {

// error handling

}

It works like this:

  1. First, the code in try {...} is executed.
  2. If there were no errors, then catch(err) is ignored: the execution reaches the end of try and goes on, skipping catch.
  3. If an error occurs, then try execution is stopped, and the control flows to the beginning of catch(err). The err variable (can use any name for it) will contain an error object with details about what happened.

So, an error inside the try {…} block does not kill the script: we have a chance to handle it in catch.

Let’s see examples.

  • An errorless example: shows alert (1) and (2):

    run
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    try {

    alert('Start of try runs'); // *!*(1) <--*/!*

    // ...no errors here

    alert('End of try runs'); // *!*(2) <--*/!*

    } catch(err) {

    alert('Catch is ignored, because there are no errors'); // (3)

    }
  • An example with an error: shows (1) and (3):

    run
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    try {

    alert('Start of try runs'); // *!*(1) <--*/!*

    *!*
    lalala; // error, variable is not defined!
    */!*

    alert('End of try (never reached)'); // (2)

    } catch(err) {

    alert(`Error has occurred!`); // *!*(3) <--*/!*

    }
header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
For `try..catch` to work, the code must be runnable. In other words, it should be valid JavaScript.

It won't work if the code is syntactically wrong, for instance it has unmatched curly braces:

```js run
try {
{{{{{{{{{{{{
} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
```

The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code.

So, `try..catch` can only handle errors that occur in the valid code. Such errors are called "runtime errors" or, sometimes, "exceptions".
header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
If an exception happens in "scheduled" code, like in `setTimeout`, then `try..catch` won't catch it:

```js run
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (e) {
alert( "won't work" );
}
```

That's because the function itself is executed later, when the engine has already left the `try..catch` construct.

To catch an exception inside a scheduled function, `try..catch` must be inside that function:
```js run
setTimeout(function() {
try {
noSuchVariable; // try..catch handles the error!
} catch {
alert( "error is caught here!" );
}
}, 1000);
```

Error object

When an error occurs, JavaScript generates an object containing the details about it. The object is then passed as an argument to catch:

1
2
3
4
5
try {
// ...
} catch(err) { // <-- the "error object", could use another word instead of err
// ...
}

For all built-in errors, the error object has two main properties:

name
Error name. For instance, for an undefined variable that’s "ReferenceError".
message
Textual message about error details.

There are other non-standard properties available in most environments. One of most widely used and supported is:

stack
Current call stack: a string with information about the sequence of nested calls that led to the error. Used for debugging purposes.

For instance:

run untrusted
1
2
3
4
5
6
7
8
9
10
11
12
13
try {
*!*
lalala; // error, variable is not defined!
*/!*
} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)

// Can also show an error as a whole
// The error is converted to string as "name: message"
alert(err); // ReferenceError: lalala is not defined
}

Optional “catch” binding

[recent browser=new]

If we don’t need error details, catch may omit it:

1
2
3
4
5
try {
// ...
} catch { // <-- without (err)
// ...
}

Using “try..catch”

Let’s explore a real-life use case of try..catch.

As we already know, JavaScript supports the JSON.parse(str) method to read JSON-encoded values.

Usually it’s used to decode data received over the network, from the server or another source.

We receive it and call JSON.parse like this:

run
1
2
3
4
5
6
7
8
9
let json = '{"name":"John", "age": 30}'; // data from the server

*!*
let user = JSON.parse(json); // convert the text representation to JS object
*/!*

// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30

You can find more detailed information about JSON in the info:json chapter.

If json is malformed, JSON.parse generates an error, so the script “dies”.

Should we be satisfied with that? Of course not!

This way, if something’s wrong with the data, the visitor will never know that (unless they open the developer console). And people really don’t like when something “just dies” without any error message.

Let’s use try..catch to handle the error:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let json = "{ bad json }";

try {

*!*
let user = JSON.parse(json); // <-- when an error occurs...
*/!*
alert( user.name ); // doesn't work

} catch (e) {
*!*
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request it one more time." );
alert( e.name );
alert( e.message );
*/!*
}

Here we use the catch block only to show the message, but we can do much more: send a new network request, suggest an alternative to the visitor, send information about the error to a logging facility, … . All much better than just dying.

Throwing our own errors

What if json is syntactically correct, but doesn’t have a required name property?

Like this:

run
1
2
3
4
5
6
7
8
9
10
11
12
let json = '{ "age": 30 }'; // incomplete data

try {

let user = JSON.parse(json); // <-- no errors
*!*
alert( user.name ); // no name!
*/!*

} catch (e) {
alert( "doesn't execute" );
}

Here JSON.parse runs normally, but the absence of name is actually an error for us.

To unify error handling, we’ll use the throw operator.

“Throw” operator

The throw operator generates an error.

The syntax is:

1
throw <error object>

Technically, we can use anything as an error object. That may be even a primitive, like a number or a string, but it’s better to use objects, preferably with name and message properties (to stay somewhat compatible with built-in errors).

JavaScript has many built-in constructors for standard errors: Error, SyntaxError, ReferenceError, TypeError and others. We can use them to create error objects as well.

Their syntax is:

1
2
3
4
5
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...

For built-in errors (not for any objects, just for errors), the name property is exactly the name of the constructor. And message is taken from the argument.

For instance:

run
1
2
3
4
let error = new Error("Things happen o_O");

alert(error.name); // Error
alert(error.message); // Things happen o_O

Let’s see what kind of error JSON.parse generates:

run
1
2
3
4
5
6
7
8
try {
JSON.parse("{ bad json o_O }");
} catch(e) {
*!*
alert(e.name); // SyntaxError
*/!*
alert(e.message); // Unexpected token b in JSON at position 2
}

As we can see, that’s a SyntaxError.

And in our case, the absence of name is an error, as users must have a name.

So let’s throw it:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let json = '{ "age": 30 }'; // incomplete data

try {

let user = JSON.parse(json); // <-- no errors

if (!user.name) {
*!*
throw new SyntaxError("Incomplete data: no name"); // (*)
*/!*
}

alert( user.name );

} catch(e) {
alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}

In the line (*), the throw operator generates a SyntaxError with the given message, the same way as JavaScript would generate it itself. The execution of try immediately stops and the control flow jumps into catch.

Now catch became a single place for all error handling: both for JSON.parse and other cases.

Rethrowing

In the example above we use try..catch to handle incorrect data. But is it possible that another unexpected error occurs within the try {...} block? Like a programming error (variable is not defined) or something else, not just this “incorrect data” thing.

For example:

run
1
2
3
4
5
6
7
8
9
10
let json = '{ "age": 30 }'; // incomplete data

try {
user = JSON.parse(json); // <-- forgot to put "let" before user

// ...
} catch(err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (no JSON Error actually)
}

Of course, everything’s possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades – suddenly a bug may be discovered that leads to terrible hacks.

In our case, try..catch is meant to catch “incorrect data” errors. But by its nature, catch gets all errors from try. Here it gets an unexpected error, but still shows the same "JSON Error" message. That’s wrong and also makes the code more difficult to debug.

Fortunately, we can find out which error we get, for instance from its name:

run
1
2
3
4
5
6
7
try {
user = { /*...*/ };
} catch(e) {
*!*
alert(e.name); // "ReferenceError" for accessing an undefined variable
*/!*
}

The rule is simple:

Catch should only process errors that it knows and “rethrow” all others.

The “rethrowing” technique can be explained in more detail as:

  1. Catch gets all errors.
  2. In the catch(err) {...} block we analyze the error object err.
  3. If we don’t know how to handle it, we do throw err.

In the code below, we use rethrowing so that catch only handles SyntaxError:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let json = '{ "age": 30 }'; // incomplete data
try {

let user = JSON.parse(json);

if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}

*!*
blabla(); // unexpected error
*/!*

alert( user.name );

} catch(e) {

*!*
if (e.name == "SyntaxError") {
alert( "JSON Error: " + e.message );
} else {
throw e; // rethrow (*)
}
*/!*

}

The error throwing on line (*) from inside catch block “falls out” of try..catch and can be either caught by an outer try..catch construct (if it exists), or it kills the script.

So the catch block actually handles only errors that it knows how to deal with and “skips” all others.

The example below demonstrates how such errors can be caught by one more level of try..catch:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function readData() {
let json = '{ "age": 30 }';

try {
// ...
*!*
blabla(); // error!
*/!*
} catch (e) {
// ...
if (e.name != 'SyntaxError') {
*!*
throw e; // rethrow (don't know how to deal with it)
*/!*
}
}
}

try {
readData();
} catch (e) {
*!*
alert( "External catch got: " + e ); // caught it!
*/!*
}

Here readData only knows how to handle SyntaxError, while the outer try..catch knows how to handle everything.

try..catch..finally

Wait, that’s not all.

The try..catch construct may have one more code clause: finally.

If it exists, it runs in all cases:

  • after try, if there were no errors,
  • after catch, if there were errors.

The extended syntax looks like this:

1
2
3
4
5
6
7
*!*try*/!* {
... try to execute the code ...
} *!*catch*/!*(e) {
... handle errors ...
} *!*finally*/!* {
... execute always ...
}

Try running this code:

run
1
2
3
4
5
6
7
8
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
}

The code has two ways of execution:

  1. If you answer “Yes” to “Make an error?”, then try -> catch -> finally.
  2. If you say “No”, then try -> finally.

The finally clause is often used when we start doing something and want to finalize it in any case of outcome.

For instance, we want to measure the time that a Fibonacci numbers function fib(n) takes. Naturally, we can start measuring before it runs and finish afterwards. But what if there’s an error during the function call? In particular, the implementation of fib(n) in the code below returns an error for negative or non-integer numbers.

The finally clause is a great place to finish the measurements no matter what.

Here finally guarantees that the time will be measured correctly in both situations – in case of a successful execution of fib and in case of an error in it:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let num = +prompt("Enter a positive integer number?", 35)

let diff, result;

function fib(n) {
if (n < 0 || Math.trunc(n) != n) {
throw new Error("Must not be negative, and also an integer.");
}
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}

let start = Date.now();

try {
result = fib(num);
} catch (e) {
result = 0;
*!*
} finally {
diff = Date.now() - start;
}
*/!*

alert(result || "error occurred");

alert( `execution took ${diff}ms` );

You can check by running the code with entering 35 into prompt – it executes normally, finally after try. And then enter -1 – there will be an immediate error, and the execution will take 0ms. Both measurements are done correctly.

In other words, the function may finish with return or throw, that doesn’t matter. The finally clause executes in both cases.

header
1
2
3
Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`.

Otherwise, if we declared `let` in `try` block, it would only be visible inside of it.
header
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
The `finally` clause works for *any* exit from `try..catch`. That includes an explicit `return`.

In the example below, there's a `return` in `try`. In this case, `finally` is executed just before the control returns to the outer code.

```js run
function func() {

try {
*!*
return 1;
*/!*

} catch (e) {
/* ... */
} finally {
*!*
alert( 'finally' );
*/!*
}
}

alert( func() ); // first works alert from finally, and then this one
```
header
1
2
3
4
5
6
7
8
9
10
11
12
13
14

The `try..finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized.

```js
function func() {
// start doing something that needs completion (like measurements)
try {
// ...
} finally {
// complete that thing even if all dies
}
}
```
In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` works before the execution flow leaves the function.

Global catch

header
1
The information from this section is not a part of the core JavaScript.

Let’s imagine we’ve got a fatal error outside of try..catch, and the script died. Like a programming error or some other terrible thing.

Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don’t see error messages), etc.

There is none in the specification, but environments usually provide it, because it’s really useful. For instance, Node.js has process.on("uncaughtException") for that. And in the browser we can assign a function to the special window.onerror property, that will run in case of an uncaught error.

The syntax:

1
2
3
window.onerror = function(message, url, line, col, error) {
// ...
};
message
Error message.
url
URL of the script where error happened.
line, col
Line and column numbers where error happened.
error
Error object.

For instance:

run untrusted refresh height
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
*!*
window.onerror = function(message, url, line, col, error) {
alert(`${message}\n At ${line}:${col} of ${url}`);
};
*/!*

function readData() {
badFunc(); // Whoops, something went wrong!
}

readData();
</script>

The role of the global handler window.onerror is usually not to recover the script execution – that’s probably impossible in case of programming errors, but to send the error message to developers.

There are also web-services that provide error-logging for such cases, like https://errorception.com or http://www.muscula.com.

They work like this:

  1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages.
  2. That JS script sets a custom window.onerror function.
  3. When an error occurs, it sends a network request about it to the service.
  4. We can log in to the service web interface and see errors.

Summary

The try..catch construct allows to handle runtime errors. It literally allows to “try” running the code and “catch” errors that may occur in it.

The syntax is:

1
2
3
4
5
6
7
8
try {
// run this code
} catch(err) {
// if an error happened, then jump here
// err is the error object
} finally {
// do in any case after try/catch
}

There may be no catch section or no finally, so shorter constructs try..catch and try..finally are also valid.

Error objects have following properties:

  • message – the human-readable error message.
  • name – the string with error name (error constructor name).
  • stack (non-standard, but well-supported) – the stack at the moment of error creation.

If an error object is not needed, we can omit it by using catch { instead of catch(err) {.

We can also generate our own errors using the throw operator. Technically, the argument of throw can be anything, but usually it’s an error object inheriting from the built-in Error class. More on extending errors in the next chapter.

Rethrowing is a very important pattern of error handling: a catch block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn’t know.

Even if we don’t have try..catch, most environments allow us to setup a “global” error handler to catch errors that “fall out”. In-browser, that’s window.onerror.

Custom errors, extending Error

When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need HttpError, for database operations DbError, for searching operations NotFoundError and so on.

Our errors should support basic error properties like message, name and, preferably, stack. But they also may have other properties of their own, e.g. HttpError objects may have a statusCode property with a value like 404 or 403 or 500.

JavaScript allows to use throw with any argument, so technically our custom error classes don’t need to inherit from Error. But if we inherit, then it becomes possible to use obj instanceof Error to identify error objects. So it’s better to inherit from it.

As the application grows, our own errors naturally form a hierarchy. For instance, HttpTimeoutError may inherit from HttpError, and so on.

Extending Error

As an example, let’s consider a function readUser(json) that should read JSON with user data.

Here’s an example of how a valid json may look:

1
let json = `{ "name": "John", "age": 30 }`;

Internally, we’ll use JSON.parse. If it receives malformed json, then it throws SyntaxError. But even if json is syntactically correct, that doesn’t mean that it’s a valid user, right? It may miss the necessary data. For instance, it may not have name and age properties that are essential for our users.

Our function readUser(json) will not only read JSON, but check (“validate”) the data. If there are no required fields, or the format is wrong, then that’s an error. And that’s not a SyntaxError, because the data is syntactically correct, but another kind of error. We’ll call it ValidationError and create a class for it. An error of that kind should also carry the information about the offending field.

Our ValidationError class should inherit from the built-in Error class.

That class is built-in, but here’s its approximate code so we can understand what we’re extending:

1
2
3
4
5
6
7
8
// The "pseudocode" for the built-in Error class defined by JavaScript itself
class Error {
constructor(message) {
this.message = message;
this.name = "Error"; // (different names for different built-in error classes)
this.stack = <call stack>; // non-standard, but most environments support it
}
}

Now let’s inherit ValidationError from it and try it in action:

run untrusted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*!*
class ValidationError extends Error {
*/!*
constructor(message) {
super(message); // (1)
this.name = "ValidationError"; // (2)
}
}

function test() {
throw new ValidationError("Whoops!");
}

try {
test();
} catch(err) {
alert(err.message); // Whoops!
alert(err.name); // ValidationError
alert(err.stack); // a list of nested calls with line numbers for each
}

Please note: in the line (1) we call the parent constructor. JavaScript requires us to call super in the child constructor, so that’s obligatory. The parent constructor sets the message property.

The parent constructor also sets the name property to "Error", so in the line (2) we reset it to the right value.

Let’s try to use it in readUser(json):

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}

// Usage
function readUser(json) {
let user = JSON.parse(json);

if (!user.age) {
throw new ValidationError("No field: age");
}
if (!user.name) {
throw new ValidationError("No field: name");
}

return user;
}

// Working example with try..catch

try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
*!*
alert("Invalid data: " + err.message); // Invalid data: No field: name
*/!*
} else if (err instanceof SyntaxError) { // (*)
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // unknown error, rethrow it (**)
}
}

The try..catch block in the code above handles both our ValidationError and the built-in SyntaxError from JSON.parse.

Please take a look at how we use instanceof to check for the specific error type in the line (*).

We could also look at err.name, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// ...
// instead of (err instanceof SyntaxError)
} else if (err.name == "SyntaxError") { // (*)
// ...
```

The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof.

Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or other unknown ones) should fall through.

## Further inheritance

The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age`). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing.

```js run
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}

*!*
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.name = "PropertyRequiredError";
this.property = property;
}
}
*/!*

// Usage
function readUser(json) {
let user = JSON.parse(json);

if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}

return user;
}

// Working example with try..catch

try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) {
*!*
alert("Invalid data: " + err.message); // Invalid data: No property: name
alert(err.name); // PropertyRequiredError
alert(err.property); // name
*/!*
} else if (err instanceof SyntaxError) {
alert("JSON Syntax Error: " + err.message);
} else {
throw err; // unknown error, rethrow it
}
}

The new class PropertyRequiredError is easy to use: we only need to pass the property name: new PropertyRequiredError(property). The human-readable message is generated by the constructor.

Please note that this.name in PropertyRequiredError constructor is again assigned manually. That may become a bit tedious – to assign this.name = <class name> in every custom error class. We can avoid it by making our own “basic error” class that assigns this.name = this.constructor.name. And then inherit all our custom errors from it.

Let’s call it MyError.

Here’s the code with MyError and other custom error classes, simplified:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyError extends Error {
constructor(message) {
super(message);
*!*
this.name = this.constructor.name;
*/!*
}
}

class ValidationError extends MyError { }

class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.property = property;
}
}

// name is correct
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

Now custom errors are much shorter, especially ValidationError, as we got rid of the "this.name = ..." line in the constructor.

Wrapping exceptions

The purpose of the function readUser in the code above is “to read the user data”. There may occur different kinds of errors in the process. Right now we have SyntaxError and ValidationError, but in the future readUser function may grow and probably generate other kinds of errors.

The code which calls readUser should handle these errors. Right now it uses multiple ifs in the catch block, that check the class and handle known errors and rethrow the unknown ones. But if the readUser function generates several kinds of errors, then we should ask ourselves: do we really want to check for all error types one-by-one in every code that calls readUser?

Often the answer is “No”: the outer code wants to be “one level above all that”, it just wants to have some kind of “data reading error” – why exactly it happened is often irrelevant (the error message describes it). Or, even better, it could have a way to get the error details, but only if we need to.

So let’s make a new class ReadError to represent such errors. If an error occurs inside readUser, we’ll catch it there and generate ReadError. We’ll also keep the reference to the original error in its cause property. Then the outer code will only have to check for ReadError.

Here’s the code that defines ReadError and demonstrates its use in readUser and try..catch:

run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = 'ReadError';
}
}

class ValidationError extends Error { /*...*/ }
class PropertyRequiredError extends ValidationError { /* ... */ }

function validateUser(user) {
if (!user.age) {
throw new PropertyRequiredError("age");
}

if (!user.name) {
throw new PropertyRequiredError("name");
}
}

function readUser(json) {
let user;

try {
user = JSON.parse(json);
} catch (err) {
*!*
if (err instanceof SyntaxError) {
throw new ReadError("Syntax Error", err);
} else {
throw err;
}
*/!*
}

try {
validateUser(user);
} catch (err) {
*!*
if (err instanceof ValidationError) {
throw new ReadError("Validation Error", err);
} else {
throw err;
}
*/!*
}

}

try {
readUser('{bad json}');
} catch (e) {
if (e instanceof ReadError) {
*!*
alert(e);
// Original error: SyntaxError: Unexpected token b in JSON at position 1
alert("Original error: " + e.cause);
*/!*
} else {
throw e;
}
}

In the code above, readUser works exactly as described – catches syntax and validation errors and throws ReadError errors instead (unknown errors are rethrown as usual).

So the outer code checks instanceof ReadError and that’s it. No need to list all possible error types.

The approach is called “wrapping exceptions”, because we take “low level exceptions” and “wrap” them into ReadError that is more abstract and more convenient to use for the calling code. It is widely used in object-oriented programming.

Summary

  • We can inherit from Error and other built-in error classes normally. We just need to take care of the name property and don’t forget to call super.
  • We can use instanceof to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from a 3rd-party library and there’s no easy way to get its class. Then name property can be used for such checks.
  • Wrapping exceptions is a widespread technique: a function handles low-level exceptions and creates higher-level errors instead of various low-level ones. Low-level exceptions sometimes become properties of that object like err.cause in the examples above, but that’s not strictly required.