16

Currently I stumbled upon the ContentChildren decorator of Angular. In the first code example the following syntax is used:

import {AfterContentInit, ContentChildren, Directive, QueryList} from '@angular/core';

@Directive({selector: 'child-directive'})
class ChildDirective {
}

@Directive({selector: 'someDir'})
class SomeDir implements AfterContentInit {
  @ContentChildren(ChildDirective) contentChildren !: QueryList<ChildDirective>;  // this line is relevant

  ngAfterContentInit() {
    // contentChildren is set
  }
}

Note the exclamation mark followed by a colon right after the @ContentChildren(ChildDirective) contentChildren variable definition. In this StackOverflow thread I discovered that this syntax can be used as a "non-null assertion operator" when accessing a variable or object property.

My question is now whether the exclamation mark before a type definition has exactly the same meaning like in a normal context. Does it simply say the TypeScript compiler: "Okay, don't worry about null or undefined", or does this syntax have another specific meaning in this context?

SparkFountain
  • 2,110
  • 15
  • 35

1 Answers1

39

My question is now whether the exclamation mark before a type definition has exactly the same meaning like in a normal context.

No that's actually not the same thing, in this context it does something different. Normally when you declare a member (which doesnt' include undefined in it's type) it has to be initialized directly or in the constructor. If you add ! after the name, TypeScript will ignore this and not show an error if you don't immediately initialize it:

class Foo {
  foo: string; // error: Property 'foo' has no initializer and is not definitely assigned in the constructor.
  bar!: string; // no error
}

The same thing actually applies to local variables as well:

let foo: string;
let bar!: string;

console.log(foo); // error: Variable 'foo' is used before being assigned.
console.log(bar); // no error

Playground

lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • 6
    This feature was added in TS2.7. Relevant documentation: [definite assignment assertions with `!`](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions) – jcalz Feb 05 '20 at 17:33
  • "Normally when you declare a member (which doesnt' include undefined in it's type) it has to be initialized directly or in the constructor" I believe by default it isn't. Only when `strictPropertyInitialization` is turned on directly or using `strict`. – Alexey Romanov Sep 05 '22 at 10:16