facebook

Blog

Stay updated

More maintainable and performant Angular apps with version 10
Angular 10: what has changed in the last three months?
Wednesday, July 15, 2020

A few months after the version 9 release (described here), the Angular team surprised us with the version 10, trying to bridge the delays and promising the release of version 11 for the end of fall.

In its development dependencies, Typescript moved to version 3.9.5. In my opinion, there is a tendency to give little importance to the detail of the version of a language, but it is undeniable that an average developer learned Typescript 3-4 years ago, when the ng cli used a 2.something version. Today’s language is entirely different and more sophisticated and people should study it again and fill the gap. The risk is to consult Angular documentation, find a bizarre copy snippet, and copy it into your project with no idea of what’s going on.

I want to show you some examples (not related to the 3.9.4 version of Typescript).
Let’s start from the two following interfaces:

export interface Student {
    id: number;
    age: number;
    name: string;
    surname: string;
}
  
export interface Worker {
    companyId: number;
    salary: number;
}

We can define a new type that can be a Student or a Worker!

export type MyUnionType = Student | Worker;

What happens if we need to know if a variable is a Student or a Worker? We can write a function returning a type predicate:

isStudent(person: MyUnionType): person is Student {
    return (person as Student).name !== undefined;
}

and then using it in the code

if (this.isStudent(person)) {
    console.log(person.name);
} else {
    // ....
}

It’s interesting to point out that in the else we have a Worker explicitly.

We can also define a type as a combination of two or more types.

export type MyIntersectionType = Student & Worker;

There are other more advanced generic types. Consider, for example, Partial<T>.

Suppose to writing a function updating a Worker:

const updateWorker = (id: number, worker: Worker) => {};
updateWorker(1, { salary: 1000});

This code does not compile because companyId lacks in the object we passed to the function. We can correct the code in the following way:

const updateWorker = (id: number, worker: Partial<Worker>) => {};

and the error is removed.

We also have other utility types, such as Record<K,T>. Let’s define an alias type called

Headquarter and associate to each headquarter a Worker.

export type Headquarter = 'Napoli' | 'Roma' | 'Milano';
const x: Record<Headquarter, Worker> = {
    Napoli: { companyId: 3, salary: 3000 },
    Roma: { companyId: 4, salary: 3000 },
    Milano: { companyId: 5, salary: 3000 },
};

What if I wanted to create a new type starting from Student but choosing only some of its properties?

export type StudentPreview = Pick<Student, 'id' | 'surname'>;

If you are interested in learning more about Composition in Typescript, I suggest the following video, besides the official documentation.

Let’s come back to Angular 10 with a new option available for the command ng new creating a new project: ng new –script.

We not only improve the maintainability of the project by finding bugs as quickly as possible, but, above all, we allow the CLI to perform advanced optimizations on our app. In details:

  • the strict mode in Typescript is enabled ( bye-bye to variables any)
  • the type check inside the template is enabled
  • more restrictive linting rules are enabled (hoping you don’t send in review some code full of ignored suggestions)
  • bundle budget default values have been reduced by 75%
  • our app is configured as side-effect free improving the tree-shaking algorithm efficiency

The bundle budget is a little-known feature of Angular that allows you to set a threshold on the size of the application bundles. Ultimately, we want a warning or that the compilation will fail if we have exceeded that limit. Budgets are set in the angular.json file. The template created by the CLI sets the following values:

"budgets": [
    {
        "type": "initial",
         "maximumWarning": "500kb",
         "maximumError": "1mb"
    },
    {
         "type": "anyComponentStyle",
         "maximumWarning": "2kb",
         "maximumError": "4kb"
    }
]

Here you can find the full documentation about this feature.

The Typescript configuration available in a new project has changed. Now we have a new file called tsconfig.base.json, which is added to tsconfig.json, tsconfig.app.json, and tsconfig.spec.json. The idea is to give more support to editors and build tools.

In this version the tsconfig.json is emptied and becomes only a container of file paths:

/*
 This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
 It is not intended to be used to perform a compilation.
  
 To learn more about this file see: https://angular.io/config/solution-tsconfig.
*/
{
  "files": [],
  "references": [
   {
    "path": "./tsconfig.app.json"
   },
   {
    "path": "./tsconfig.spec.json"
   },
   {
    "path": "./e2e/tsconfig.json"
   }
  ]
}

The term “Solution Style”, used in the comment, suggests classic IDEs for code development. It will, therefore, be interesting to see if the Angular team will go this way. In this configuration, the application code (managed by tsconfig.app.json) is clearly isolated from the testing code (managed by tsconfig.spec.json). Most of the configuration code is written in tsconfig.base.json (where we note the strict option we mentioned earlier). All paths and files to be included are in the tsconfig.app.json file.

/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
 "extends": "./tsconfig.base.json",
 "compilerOptions": {
 "outDir": "./out-tsc/app",
  "types": []
},
 "files": [
   "src/main.ts",
   "src/polyfills.ts"
 ],
 "include": [
   "src/**/*.d.ts"
 ]
}

The list of browsers available in the file (.browserslistrc) has been updated, excluding the oldest and least used ones.

  • last 1 Chrome version
  • last 1 Firefox version
  • last 2 Edge major versions
  • last 2 Safari major version
  • last 2 iOS major versions
  • Firefox ESR
  • not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the ‘not’ prefix on this line.
  • not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the ‘not’ prefix on this line.

We can check the full list launching the command npx browserslist

  • chrome 83
  • edge 83
  • edge 81
  • firefox 78
  • firefox 68
  • ios_saf 13.4-13.5
  • ios_saf 13.3
  • ios_saf 13.2
  • ios_saf 13.0-13.1
  • ios_saf 12.2-12.4
  • ios_saf 12.0-12.1
  • safari 13.1
  • safari 13
  • safari 12.1
  • safari 12

Did you expect a longer list? The most important side effect is that ES5 builds are disabled by default. To enable the ES5 build on an older browser (e.g., Internet Explorer), the .browserlistrc file must first be updated.

The Classes using Angular features without specifying a decorator (e.g., @Component, @Directive, @Module) don’t compile with Ivy. For example, consider the following code snippet (source)

class Base {
  @Input()
  foo: string;
}
  
@Directive(...)
class Dir extends Base {
  ngOnChanges(): void {}
}

When an Angular decorator is missing on a class, the Ivy compiler does not add all the extra code for dependency injection. As described in the official documentation, the daughter class will inherit from the father a builder without all the necessary additional information. Therefore, when Angular tries to instantiate it, it will fail.

In the View Engine, the compiler has a global set of data and can, therefore, retrieve the missing information. But the Ivy compiler processes each directive in isolation (to be faster) and, therefore, a global support, which could help it, lacks.

There is big news about support for Bazel, a build tool used in Google that has been made available in preview in Angular for more than a year. The news is that Bazel will NEVER be the build tool in Angular and that the project has been completely shelved. An article by Alex Eagle explains in detail what happened. I find the phrase “Angular apps don’t suffer from the problems that Bazel tries to solve” meaningful. Therefore it is useless to introduce new complexity and tools in the build process in order not to obtain significant improvements.

The Angular Package Format used not only in the additional packages (for example Angular Material) but also in those contained in the whole @angular namespace (for example, @angular/core, @angular/ forms etc.) has been emptied of all the bundles that previously they were needed for ES5 compilation. This results in a 119 MB reduction in the download triggered by npm install.

What else is left compared to version 9? The resolution of over 700 issues related not only to the base library but also to the tooling part (the ng cli). The link that describes how to migrate an application to version 10 is the following.

In conclusion, the new version of Angular does not introduce revolutions in JavaScript programming but simply goes to stabilize and optimize a framework that we can now consider mature. What will happen in the coming years is impossible to predict (as this 2020 teaches us). Will new actor take over to overturn the hegemony of React and Angular (Vue seems already forgotten)? Will WebAssembly be instrumental in the future of web frameworks? We’ll see!

See you next!

Written by

Salvatore Sorrentino