Reputation: 1078
I have need of an interface or type that requires the property updatedAt
if the property updatedBy
is present (and vis a versa); however, it should allow for neither property to be set.
interface BaseRow {
id: string
createdAt: string
createdBy: string
}
interface Updated {
updatedAt: string
updatedBy: string
}
interface UpdatedRow extends BaseRow, Updated {}
type Row = BaseRow | UpdatedRow
Based on the above, I would expect that the following would cause the typescript compiler to throw an error because updatedAt
is present but updatedBy
is not.
const x: Row = {
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
The above does not however throw any error.
Why doesn't the above solution work as expected? What is the best way to achieve an interface
or type
that conditionally requires two or more properties?
Upvotes: 3
Views: 2282
Reputation: 249466
According to the PR that implements strict checking on object literals the check that is performed is :
... it is an error for the object literal to specify properties that don't exist in the target type
While the properties are on different branches of the union type, the property exists on the union type. Since the other updated property does not exist, the object literal, does not conform to the UpdatedRow
interface, but it does conform to the Row
interface, so it is assignable to the variable. This is why there is not error.
A solution, which may or may not be applicable in your case, is to ensure that an updated row is incompatible with a non updated row by adding a string property that is typed to a different string literal type on the two interfaces:
interface BaseRow {
id: string
createdAt: string
createdBy: string
}
interface Updated {
type: 'u'
updatedAt: string
updatedBy: string
}
interface UpdatedRow extends BaseRow, Updated {}
type Row = (BaseRow & { type: 'n'}) | UpdatedRow
const x: Row = { // Error, no updatedBy
type: 'u',
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
const x2: Row = { // Error, with updatedAt is not assignable to (BaseRow & { type: 'n'})
type: 'n',
id: 'someID',
createdAt: 'someISO',
createdBy: 'someID',
updatedAt: 'someISO'
}
Upvotes: 4