What’s the difference between definite assignment assertion and ambient declaration?

Solution 1:

declare name: string;

This says to the compiler:

"There is a property called name of type string. I shouldn't have to prove to you that name actually exists, but I want to use it anyway."

The declare keyword is typically used in type definition files that provide typings for files that Typescript cannot get type information from (such as plain JS files). So if I was reading your code, I would assume that name is getting monkey patched in from some JS file somewhere, and you are noting that here.

I would be incorrect.


name!: string;

This says to the compiler:

"There is a property called name with a type of string | undefined. It starts with a value of undefined. But every time I get or set that property, I want to treat it as type string."

Using this form it's clear to anyone reading the code that name is undefined at first, but is treated like a string anyway. That means it must be set in this file somewhere, just probably not in the constructor.

From what you are saying, I would be correct in those assumptions.


In practice the result is identical. In both cases you have a string property that you never have to actually initialize. However, I would argue that the name!: string is far more clear about what is actually going on.


Lastly though, I have to mention, you should probably just refactor your code so that you can assign the property in the constructor. Both those methods are not as type safe since you could potentially treat an uninitialized value as a string, which will likely cause a crash if it happens.