interface I1 {
x: number;
y: string;
}
interface I2 {
x?: number;
y?: string;
}
const tmp1: Partial<I1> = {}, tmp2: I2 = {};
Just like the example, is there an obvious difference between these two things?
CodePudding user response:
The difference is what is being expressed.
The types of tmp1
and tmp2
are the same because Partial
does the same as
type MyPartial<T> = { [K in keyof T] ?: T[K] }
Where ?
is a mapping modifier that makes a property optional.
However, other than structural compatibility, the types express *different things`:
I1
implies that the regular shape of the object expected is non-optional. Modifying it viaPartial
means that is an exception to the regular expectations.I2
is the opposite - the regular shape of the object expected is fully optional.
Illustrating with a real looking type might make it be clearer:
interface User {
age: number;
name: string;
}
Cements the decision that all User
objects must have age
and name
filled in. Otherwise they are not a valid User
. However, conceivably there can be a Partial<User>
in the system while the full set of information is still being collected. That would be a temporary state cannot be directly used with (for example)
function saveUser(user: User) { /* ... */ }
as that function requires a real user object with all the fields.
Conversely, a fully optional type might be more appropriate when any subset of the data is valid:
interface SurveyResult {
score?: number;
comment?: string;
}
Means that any survey outcome is expected. Only the score can be filled in, or the comment, or even nothing. Presumably, even an empty result still counts for the total surveys or similar.
CodePudding user response:
The existing answer addresses the direct question you had, but you might be wondering why there are multiple ways to express optional type fields like this.
There are in fact several reasons why Partial<I1>
may be used instead of I2
(this is not an exhaustive list). The first and most obvious is that if you add a new field to I1
then Partial<I1>
will automatically contain it. This is especially important in larger projects, or for cases where the type of I1
may be coming from an external dependency. Additionally Partial<I1>
is more readable for other developers who may modify the code later (or yourself, when you go looking for bugs a few months down the line). Partial<I1>
can be much more concise, especially if I1
has a lot of fields. There is also the issue of typos and other potential problems that can appear when you are duplicating code in several places. As a general rule of thumb, you should use Partial<Type1>
rather than manually creating a Type2
in scenarios like the original question.