It would be great if we had type checking inside the SimpleChanges typescript parameter of ngOnChanges hook for the current Component.
This would prevent us from errors in the properties we check.
It would be great if we had type checking inside the SimpleChanges typescript parameter of ngOnChanges hook for the current Component.
This would prevent us from errors in the properties we check.
I suggest the following solution, adapted from https://github.com/angular/angular/issues/17560#issuecomment-624161007
export type SimpleChangeTyped<T> = Omit<SimpleChange, SimpleChange['previousValue'] | SimpleChange['currentValue']>
& {
previousValue: T;
currentValue: T;
};
export type SimpleChangesTyped<T> = {
[K in keyof T]: SimpleChangeTyped<T[K]>;
};
This solution has the following advantages:
thispreviousValue or currentValue properties on SimpleChangeUsageExample:
ngOnChanges(changes: SimpleChangesTyped<YourComponent>): void { // Or use SimpleChangesTyped<this>
changes.someComponentProperty; // GOOD: Will identify the changes object with correct types
changes.someOtherProperty; // BAD: Property 'someOtherProperty' does not exist on type 'SimpleChangesTyped<YourComponent>'.ts(2339)
changes.someComponentProperty.currentValue; // GOOD: Will identify the type of the someComponentProperty property
changes.someComponentProperty.firstChange; // GOOD: Will identify the type of the SimpleChange firstChange property (boolean)
changes.someComponentProperty.someOtherProperty; // BAD: Property 'someOtherProperty' does not exist on type 'SimpleChangeTyped<SomeComponentPropertyType>'.ts(2339)
}
The only downside of this method is that it does not inherit/union of SimpleChanges and thus if any properties are added to this interface in the future they will not be valid for SimpleChangesTyped. I tried using the following variant:
export type SimpleChangesTyped<T> =
Omit<SimpleChanges, keyof T> &
{
[K in keyof T]: SimpleChangeTyped<T[K]>;
};
However sadly this does not work, since you end up allowing all key's again.
There is a solution from Netanel Basal
type MarkFunctionPropertyNames<T> = {
[Key in keyof T]: T[Key] extends Function | Subject<any> ? never : Key;
}
type ExcludeFunctionPropertyNames<T extends object> = MarkFunctionPropertyNames<T>[keyof T];
type ExcludeFunctions<T extends object> = Pick<T, ExcludeFunctionPropertyNames<T>>;
export type NgChanges<TComponent extends object, TProps = ExcludeFunctions<TComponent>> = {
[Key in keyof TProps]: {
previousValue: TProps[Key];
currentValue: TProps[Key];
firstChange: boolean;
isFirstChange(): boolean;
}
}
// ...
@Component({...})
export class MyComponent {
ngOnChanges(changes: NgChanges<MyComponent>) { }
}