What sets apart the symbols '!: ' and '?: ' in TypeScript object declarations?

Class Employee {
  firstName: string;
  lastName!: string;
  middleName?: string;
}

Can you explain the significance of the different field declarations within the Employee class?

Check out a Live Example

Answer №1

When the compiler option strictNullChecks: false is enabled

If you have strictNullChecks: false set in your tsconfig.json, then both scenarios are essentially the same because having strictNullChecks disabled allows all fields to accept null or undefined as valid values.

When the compiler option strictNullChecks: true is enabled

class Employee {
   firstName: string;
   lastName!: string;
   middleName?: string;
}

firstName: string specifies that firstName must be of type string. It cannot be assigned a value of null or undefined. All uninitialized fields will default to undefined, triggering an error message stating

Property 'firstName' has no initializer and is not definitely assigned in constructor
.

To resolve this error, you can either initialize like so:

firstName: string = 'Some default value'
or include a constructor function where the value is assigned.

constructor() {
    this.firstName = 'some default value';
}

Now regarding the use of the ! syntax. The lastName!: string syntax denotes that only a value of type string is allowed for lastName; null and undefined are not permissible. Although it prevents the definite assignment error from being raised by the compiler. Consider the following example:

   class Employee {
       firstName: string;
       lastName!: string;
       middleName?: string;

      constructor() {
          // This suppresses the compiler error about first name not being initialized
          this.firstName = 'some default value';
          // Compiler does not recognize lastName being assigned within init() function
          this.init();
      }
      
      private init(): void {
          this.lastName = 'some default value';
      }
    }

In the above code snippet, lastName is indeed assigned in the constructor through the this.init() call. Nevertheless, the compiler remains unaware of this fact. Hence, adding the ! serves as a way to communicate to the compiler, "I know what I'm doing." It is then incumbent upon you to ensure the accuracy of your code.

As for the middleName?: string syntax. This is akin to middleName: string | undefined; Given that all values default to undefined, the compiler will not flag any issues pertaining to the uninitialized state of middleName.

Answer №2

The ? positioned there signifies that the property is optional, as explained in this resource.

The ! at that spot denotes a definite assignment assertion, which can be likened to the non-null assertion operator, but applied to properties instead of expressions as outlined in this source.

In the given example, there may be two or possibly three errors:

  1. Class should actually be written as class; keep in mind JavaScript and TypeScript are case-sensitive.

  2. An initializer is required for firstName, unless a constructor is present to assign it unconditionally.

  3. Despite using ! on lastName, indicating that it will definitely be assigned, there is no explicit assignment happening in the code snippet, leading to potential issues when trying to access its value.

Edit: The linked code addresses items #1 and #2 mentioned above, but overlooks #3. TypeScript does not highlight the fact that lastName remains unassigned, assuming it's a string when it could potentially be undefined.

Answer №3

The secrets lie deeply within the expanse of TypeScript documentation.

? can be found in the realm of interfaces, where it signifies an optional property.

! serves as the definite assertion operator, affirming to the compiler that a property is indeed set (not merely null or undefined) even when such certainty eludes TypeScript's usual analysis.


By the way, the use of Class as a keyword in TypeScript or JavaScript will trigger an error. The correct declaration for a class involves utilizing class. Remember, TypeScript and JavaScript strictly observe case sensitivity with identifiers and keywords (Class versus class denote distinct entities).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the correct method for storing a response in an array variable in Angular?

I am looking to save the response data from an API call in a variable and display it in the component.html file. Component.ts file : public coinsHistory = []; this.service.getCoinsHistory().subscribe( (response) => { this.handleCoinsRespon ...

Error encountered: The combination of NextJS and Mongoose is causing a TypeError where it is unable to read properties of undefined, specifically when trying

Versions: Next.js 14.1 React 18 I am currently developing a profile section where users can update their profile information such as username, name, and profile photo. To achieve this, I have implemented a component that contains a form (using shadcn) to ...

What are the steps for integrating angular2 starter with express?

Can someone explain how to integrate the following Angular starter template into an Express framework? https://github.com/gdi2290/angular-starter I am able to start the webpack dev server without any issues, but I would like to utilize additional librari ...

TypeScript encounters challenges with implementing Redux containers

I'm struggling to understand the correct way to type Redux containers. Let's consider a simple presentational component that looks like this: interface MyProps { name: string; selected: boolean; onSelect: (name: string) => void; } clas ...

Parent variable in Angular Type Script causing undefined error

Can anyone explain why I keep receiving an undefined error? export abstract class BaseEditorComponent implements IPropertyEditor, OnDestroy { @Input() public element: BpmnJS.IRegistryElement; --more code here export class CommonPropertiesEditorCo ...

Angular 14: Trouble with ngFor after latest update - Type 'unknown' causing issues

Just updated my project to angular version 14.0.4 Within the html of a component, I have the following code: <div class="file" *ngFor="let file of localDocumentData.files; index as i;"> <div class="card"> ...

What is the best method to adjust the width of the PrimeNG ConfirmDialog widget from a logic perspective

Currently utilizing "primeng": "^11.2.0" and implementing the ConfirmDialog code below this.confirm.confirm({ header: 'Announcement', message: this.userCompany.announcement.promptMsg, acceptLabel: this.userCompany.announcement ...

What are the possible reasons behind the malfunctioning of my attributes after I replaced my method with a new one

My Computer Science lessons have been focused on Object Orientated Programming, but the code provided as an example seems to be malfunctioning. class bankAccount(): '''This is a bank account class''' def __init__(s ...

Enhancements to managing universal configuration object across the entire application

My current project involves working on an application with multiple products. To streamline product-specific configuration and eliminate the need for excessive if-else statements, I am looking to implement product-specific config keys that are consistently ...

I'm looking for a solution to reorganize my current state in order to display the image URL

My React component, which also utilizes TypeScript, is responsible for returning a photo to its parent component: import React, { useEffect, useState } from "react"; import axios from "axios"; export const Photo = () => { const [i ...

The argument provided is a string type, which cannot be assigned to a parameter expecting an object with a 'results' property of type string

When attempting to pass the result.nativeEvent.message to another function, I am encountering the error: Argument of type 'string' is not assignable to parameter of type '{ results: string; } on onUnityMessageController(result.nativeEvent.me ...

Dynamic routing with ngIf in Angular 2's router system

Is there a way to use *ngIf with dynamic router in Angular? Let's say I have a top navigation component with a back button, and I only want the back button to be visible on the route 'item/:id'. I tried using *ngIf="router.url == '/ite ...

Create a solution that is compatible with both web browsers and Node.js

I am developing a versatile library that can be utilized in both the browser and in node environment. The project involves three json config files, with the latter two extending the tsconfig.json. tsconfig.json (contains build files) tsconfig.browser.js ...

Angular HTTP Patch method requires explicitly defined HTTP options as input parameters

I encountered a challenge with using Angular's HTTP patch method and noticed that the overloaded function patch(url, body, options) only accepts hardcoded values for HTTP options. An example of a hardcoded approach that works: patchEntity(id: number) ...

Using a For Loop in VueJS with TypeScript for an array of objects

I'm currently working on a for loop within an object array. Below is the code snippet I am working with: private async rightsOverview() { let item: any[] = []; const prod = await fetchFromApi<ProductionBaseType>(`/productions/${id ...

error: encountering issue with Vue TypeScript Jest tests - '$store' property is undefined

I've been facing issues with my tests failing after a recent npm install. I've tried adjusting versions up and down, but haven't had any success. Interestingly, the $store isn't directly used in any of the components or tests. Despit ...

Is there a way to navigate directly to a specific component without passing through the main component?

I'm currently utilizing the nativescript-urlhandler plugin in my Nativescript application. However, when I implement a router, the application routing first directs to the Main Component and then to the specific component I intend to reach. My goal i ...

The error message "TypeError: (0 , N.createContext) is not a function" indicates that

I'm currently in the process of developing a cutting-edge application using Next.js 14, TypeScript, and Telegram Open Network. However, I've hit a roadblock while attempting to incorporate the TONConnectUIProvider at the root of my app. Upon run ...

Partial specialization of member functions in C++ templates

I am experimenting with specializing geometric functions based on whether they are 2D or 3D, represented by a template parameter. Below is a snippet of some (very flawed) code for a simplistic version of the issue: template <typename T, int d> class ...

Forwarding parameter data type

I am facing an issue with 2 navigation points leading to the same screen 1. this.router.navigate([this.config.AppTree.App.Module.Details.Path], { state: { data: { id: this.TableId } } }); this.router.navigate([this.config.AppTree.App.Module.Details.Pa ...