Can you explain the distinction between certain assignment assertion and ambient declaration?

When declaring that a field is definitely initialized within a class, what distinguishes the ! (exclamation point, definite assignment assertion) from the declare modifier?

The subsequent code triggers an error in strict mode as TypeScript cannot confirm that the field has been properly initialized.

class Person {
    name: string; // Error: Property 'name' has no initializer and is not definitely assigned in the constructor.
}

I have observed 2 methods to address this:

  1. Definite assignment assertion:
    class Person {
        name!: string;
    }
    
  2. Ambient declaration:
    class Person {
        declare name: string;
    }
    

I am unable to discern the disparity between these two approaches. They both rectify the error, they both do not generate code, and they both restrict initializers. Does the introduction of ambient declaration (introduced in v3.7) render definite assignment assertions (introduced in v2.7) obsolete? Is it preferable to utilize declare over ! whenever feasible?

Answer №1

When experimenting with the type system, the `declare` keyword becomes handy for mocking values, but in real production code, its usage is minimal.


declare name: string;

This informs the compiler:

"There exists a property named `name` of type `string`. I don't need to prove its existence, but I want to utilize it anyway."

The `declare` keyword is commonly found in type definition files that offer typings for situations where Typescript cannot derive type information (like in plain JS files). Therefore, if encountered in your code, one might assume that `name` is being added dynamically from a JS file and documented here.

But assumptions could be incorrect.


name!: string;

This signals to the compiler:

"There's a property named `name` with a type of `string | undefined`. Initially set as `undefined`, however, treat it always as a `string` during access or assignment."

By using this pattern, it explicitly states that `name` starts as undefined but is handled as a string throughout. This implies that its initial setup may not be inside the constructor.

Your clarification confirms these assumptions.


In practice, within this specific case, the outcomes are nearly identical. In both scenarios, you possess an uninitialized string property. Nevertheless, arguments can be made that `name!: string` offers better clarity on what's happening.

Furthermore, it's crucial to note that `declare` does not produce any executable code; it simply establishes something in the type system. (Kudos to @NoelDeMartin for pointing this out)

class Foo {
    bar!: string;
    declare baz: string;
}

let bar: string
declare let baz: string

Compilation yields:

class Foo {
    constructor() {
        Object.defineProperty(this, "bar", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
}
let bar;

Note that `baz` is entirely omitted from the output.


Ultimately, it's advisable to refactor your code so that property assignment occurs within the constructor. Both approaches mentioned are less stringent in terms of type safety as handling an uninitialized value as a `string` might lead to unexpected runtime errors.

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

Service consuming in Angular 2 using Stomp protocol

Why am I seeing responseBody as undefined, but I am able to see the subscribe response in msg_body? What could be causing this issue with responseBody? let stomp_subscription = this._stompService.subscribe('/topic/queue'); stomp_subscription.ma ...

Updating a property value within a JSON object: Adjusting attributes in a JSON data format

How can I modify a specific key's value in a JSON array like the following example: input = [{"201708":10,"201709": 12, "metric":"attritionManaged"},{"201708":10,"201709": 12, "metric":"attritionUnManaged"},{"201708":10,"201709": 12, "metric":"EHC"}] ...

Nuxt - asyncData ISSUE: "Variable '$axios' is inferred to have an 'any' type."

Referencing the guidelines provided at Encountering an error logged in console while executing yarn dev: ERROR ERROR in pages/index.vue:51:21 ...

Combining cells for certain entries in an Angular data table

Just starting to learn angular, and here's the scenario I'm dealing with: I have a table consisting of 10 columns. Let's say column 4 contains different status categories like children, teen, young, adult, and senior. When displaying all ...

determining the data type based on the function parameter even when a specific type parameter is provided

Consider this example: type UpdateFieldValue<T extends Record<string, unknown>> = (key: keyof T, value: SomeType) => void The goal is to have SomeType represent the value type of the property (key) within object T, with key being provided t ...

The error message "Type 'string | number' is not assignable to type 'number'" indicates a type mismatch in the code, where a value can be either

I encountered an error code while working with AngularJS to create a countdown timer. Can someone please assist me? //Rounding the remainders obtained above to the nearest whole number intervalinsecond = (intervalinsecond < 10) ? "0" + intervalinseco ...

The Vue data retrieved from an API using onMounted() is not initially showing up in the DOM. However, it magically appears after I make changes to the template

Hello and thank you to those taking the time to read this. I am new to Vue, so I may be overlooking something obvious here, but after being stuck for several days, I am reaching out for help. In my SFC file, I have an onMounted function fetching data from ...

When trying to reload Angular 8 pages, encountering an error that reads "Cannot GET /login" and also receiving a notification stating the image '/favicon.ico' cannot be loaded due to a violation of

Encountering an issue with the error message "Cannot GET login/" appearing on the page body of my latest Angular 8 app. Despite attempting various solutions found on forums, I have been unable to resolve this error. Any suggestions or advice would be great ...

Submitting a POST request from a Typescript Angular 2 application to a C# MVC backend

Having trouble passing a payload using Typescript service in an http.post request Here is my TypeScript code: saveEdits(body: Object): Observable<Animal[]> { let bodyString = JSON.stringify(body); let headers = new Headers({ 'Content- ...

Numerous unspecified generic arguments

Imagine a collection of functions, each capable of taking an argument and returning a value (the specifics don't matter): function convertToNumber(input: string): number { return parseInt(input) } function convertToBoolean(input: number): boolean { ...

Utilizing TypeScript with Vue3 to Pass a Pinia Store as a Prop

My current stack includes Typescript, Pinia, and Vue3. I have a MenuButton component that I want to be able to pass a Pinia store for managing the menu open state and related actions. There are multiple menus in the application, each using the same store f ...

"Although the Set-cookie is present in the response header, it is not being properly

I developed a GraphQL server using apollo-server-express, and it is currently running on localhost:4000. Upon sending a query from GraphQL playground, the response includes a set-cookie in the header: response header However, when checking the storage &g ...

Iterate through each item in an object using Angular

I attempted to utilize a forEach loop, but it's indicating that it's undefined for some reason. Here is my code snippet: var array: MoneyDTO[] = prices array.forEach(function (money: MoneyDTO) { if (money.currency == 'QTW& ...

Gradle synchronization in IntelliJ causing missing folders in WAR Artifact

Currently, I am working on a Spring MVC application that incorporates TypeScript. The TypeScript code is transpiled using a Gradle task from the directory src/main/ts to build/ts. Subsequently, the resulting JavaScript files are added to the WAR file usin ...

Most Effective Approach for Handling Multiple Fetch Requests Concurrently using Async-Await in TypeScript?

I am currently exploring the idea of making multiple API calls simultaneously by utilizing multiple fetch requests within an await Promise.all block, as shown below: const responseData = await Promise.all([ fetch( DASHBOARDS_API + "getGoal ...

Issue with Typescript in Material-UI's CardActionArea component attribute

The documentation for Material-ui's Buttons | Third party routing library explains the need to create an adapter to wrap a react-router-dom/Link component in a Button. Interestingly, attempting the same with a CardActionArea (which is labeled as a Bas ...

Currying disrupts the inference of argument types as the argument list is divided in half, leading to confusion

One of my favorite functions transforms an object into a select option with ease. It's written like this: type OptionValue = string; type OptionLabel = string; export type Option<V extends OptionValue = OptionValue, L extends OptionLabel = OptionL ...

Having trouble with building an Ionic3 project, getting the error message: "Execution failed for task ':app:processDebugResources'. > Failed to execute aapt"

My attempt to develop an android app in ionic 3 hit a roadblock when running 'ionic cordova build android' resulted in the error: Execution failed for task ':app:processDebugResources'. > Failed to execute aapt I have integrated plug ...

Using TypeScript, a parameter is required only if another parameter is passed, and this rule applies multiple

I'm working on a concept of a distributed union type where passing one key makes other keys required. interface BaseArgs { title: string } interface FuncPagerArgs { enablePager: true limit: number count: number } type FuncArgs = (Fu ...

What is the best way to activate an alert or swal function just once instead of repeatedly?

I am just starting to learn Angular. Currently, I have an application that contains two variables related to the status of financial transactions. These variables are: tab1TrxMessage, which holds any important messages, and tab1TrxStatus that indicates wh ...