Reputation: 422
Is there a way in Typescript to optionally require keys in different circumstances?
For example
interface IDog {
name: string;
weight: number;
}
class Retriever implements IDog {
name = "Dug";
weight = 70;
public updateAttribute(props: IDog) {
...
}
}
Let's say updateAttribute
can get either objects: { name: "" }
, { weight: 500 }
, { name: "", weight: 30 }
, {}
. Right now, I would either have to add ?
to every attribute in IDog, or add ?
to (props?: IDog)
. Both aren't great solutions, because I want Retriever to require the attributes, and I want each attribute to be option in updateAttribute
.
Is there a way to update updateAttribute
to allow for any permutation of IDog, but still make all attributes required for the class?
Upvotes: 1
Views: 97
Reputation: 31873
CRice's answer is correct. Using the Partial<T>
type is the correct and idomatic approach.
However, it is worth noting that, as TypeScript is a structurally typed language, the implements
clause is a formality that does not affect type compatibility.
This means that for an interface
interface IDog {
name: string;
weight: number;
}
the following are equivalent:
class Dog implements IDog {
name = "Fred";
weight = 100;
}
and
class Dog {
name = "Fred";
weight = 100;
}
both are implementations of IDog
by definition.
It is further worth noting that, by implication, T
is an implementation of Partial<T>
.
Hence you could write the following as well (--strictNullChecks
assumed)
class Retriever {
name = "Dug";
weight = 70;
updateAttribute(props: Partial<this>) {
Object.assign(this, props);
}
}
We can further generalize this in an interesting way by making updateAttribute
return this
.
updateAttribute<U extends Partial<this>>(props: U): this & U {
return Object.assign(this, props);
}
which allows the following
class Retriever {
name = "Dug";
weight = 70;
age?: number;
updateAttribute<U extends Partial<this>>(props: U): this & U {
return Object.assign(this, props);
}
}
new Retriever().age.toFixed(); // error possibly undefined.
new Retriever().updateAttribute({age: 30}).age.toFixed(); // OK
Upvotes: 1
Reputation: 32266
Sounds like you're looking for the Partial
type. The partial type is a generic type which makes all the fields of it's argument optional. So for your example:
interface IDog {
name: string;
weight: number;
}
class Retriever implements IDog {
name = "Dug";
weight = 70;
public updateAttribute(props: Partial<IDog>) {
...
}
}
Should work. For clarity, Partial<IDog>
will be equivalent to the type:
Partial<IDog> = {
name?: string;
weight?: number;
}
Upvotes: 2