My interpretation of the query is as follows:
When merging an interface declaration into the instance side of a class declaration, why doesn't the compiler issue a warning when the properties declared in the interface are not initialized in the class? This happens even when the --strictPropertyInitialization compiler option is turned on.
Here's the answer:
The purpose of declaration merging into a class is to extend an existing class externally. Generally, if you add a new property or method to the class interface from outside, you should also initialize that property or method externally. You can start with the original class declaration:
class Dog {
constructor() { }
}
Then, externally augment the interface and implement the added properties like so:
interface Animal {
a: string;
b: string;
}
interface Dog extends Animal {
c: string;
}
// implementation
Dog.prototype.a = "defaultA";
Dog.prototype.b = "defaultB";
Dog.prototype.c = "defaultC";
If you follow this approach, everything will function as expected:
const d = new Dog();
console.log(d.b); // "defaultB"
d.b = "newB";
console.log(d.b); // "newB"
On the other hand, the --strictPropertyInitialization compiler option is specifically designed to ensure that properties declared within the class itself are properly initialized. This serves as a separate use case from declaration merging; any properties that must be initialized within the class body should be declared there as well.
That sums up the response to the initial question. It appears that there is a deeper requirement to create a class constructor from an interface without re-declaring the properties in the class. In such cases, using declaration merging might not provide the desired solution (at least in TypeScript 4.9).
There are alternative methods available; for example, I sometimes utilize what I refer to as an "assigning constructor" factory that needs to be implemented only once:
function AssignCtor<T extends object>(): new (init: T) => T {
return class { constructor(init: any) { Object.assign(this, init); } } as any;
You can then employ this factory to generate class constructors like this:
interface IDog {
a: string;
b: string;
c: string;
}
class Dog extends AssignCtor<IDog>() {
bark() {
console.log("I SAY " + this.a + " " + this.b + " " + this.c + "!")
}
};
const d = new Dog({ a: "a", b: "b", c: "c" });
d.bark(); // I SAY a b c!
This may or may not address your specific requirements, and it goes beyond the scope of the original question. However, the key point is that exploring options other than declaration merging might be more beneficial in addressing your particular situation.
Playground link to code