https://netbasal.com/getting-to-know-the-partial-type-in-typescript-ecfcfbc87cb6
I don’t usually bother to write about such small things, but I’ve come across many people who don’t know this handy feature, so I decided I should.
Let’s say we have a UserModel
interface:
interface UserModel {
email: string;
password: string;
address: string;
phone: string;
}
And a User
class with update()
method:
class User {
update( user: UserModel ) {
// Update user
}
}
The problem with the code above is that we must pass an object that implements the whole UserModel
interface, otherwise typescript will be 😡.
But in our case, we want to be dynamic and not be committed to the entire interface, but still get IntelliSense.
TypeScript (v2.1) provides us with a solution precisely for these cases — The Partial
interface. All we need to do is to change the code above to this:
class User {
update( user: Partial<UserModel> ) {
// Update user
}
}
Now we can have the best of both worlds.
Another useful example would be if you have a component that takes configuration object as Input()
and you want to have a default value.
type ComponentConfig = {
optionOne: string;
optionTwo: string;
optionThree: string;
}export class SomeComponent { private _defaultConfig: Partial<ComponentConfig> = {
optionOne: '...'
} @Input() config: ComponentConfig;
ngOnInit() {
const merged = { ...this._defaultConfig, ...this.config };
}
}
Under the hood the Partial
interface looks like this:
type Partial<T> = { [P in keyof T]?: T[P]; };
You can read more about the keyof
feature here.
https://stackoverflow.com/questions/54986332/typescript-class-extending-partial-interface
The problem is that Partial<T>
will only allow you to implement the members will not require you to do so, and if you don't implement the member it will not be in the class.
You can create a function that returns a class and this class will implement the interface. The returned class will not actually have to declare any of the fields so they will all be undefined
but this should be fine since the fields have to be optional anyway.
interface Animal {
name: string;
}
type OptionalAnimal = Partial<Animal>;
function autoImplement<T>(): new () => T {
return class { } as any;
}
class Dog extends autoImplement<OptionalAnimal>() {
public constructor() {
super();
}
public breed: string;
}
var spot = new Dog();
spot.name = "Spot";
You can also cast the Dog
class to specify that the returned instance has the members of Animal
but these new members will not be accessible from inside the class:
interface Animal {
name: string;
}
class _Dog {
public constructor() {
}
public breed: string;
}
const Dog = _Dog as { new(): _Dog & Partial<Animal> } & typeof _Dog
type Dog = InstanceType<typeof Dog>
var spot = new Dog();
spot.name = "Spot";