The return value of XMLValidator.validate is either true
or ValidationError
(which is not exactly correct, check my update), which has err
property.
validate( xmlData: string, options?: validationOptionsOptional): true | ValidationError
Because javascript uses object wrappers for boolean primitive I can just check whether the err property exists or not to see if the validation succeeds.
const result = XMLValidator.validate(message)
if (result.err) { // How do I make it work in ts?
console.log(`invalid XML)
} else {
console.log(`XML)
}
But typescript won’t let me do that, it will complain Property 'err' does not exist on type 'true'
I don’t like the idea of checking the return type first because I feel it is wordy and there is no return value type definition actually (check my update). How do I write the ts code here as concise as my js code?
— update —
I further check validator.js code
const isValid = validateAttributeString(attrStr, options);
if (isValid !== true) {
return getErrorObject(...);
}
...
function getErrorObject(code, message, lineNumber) {
return {
err: {
code: code,
msg: message,
line: lineNumber.line || lineNumber,
col: lineNumber.col,
},
};
}
So it seems that I can only do if (typeof result == "boolean")
here but I hope there is any "general" solution to my question.
2
Answers
You’re right that TypeScript won’t let you access a property that it doesn’t know can exist on a variable’s type. If that type is a union, then the property must exist on all members of that union before TypeScript will let you access it.
But it is possible to narrow the type of a variable through various methods. For example, you could first check if the value is
true
, and if it’s not then TypeScript will narrow the union down to justValidationError
.Normally, another option would be to use the
in
operator, but in this case the union doesn’t only contain object types so TypeScript won’t allow it.You could also define a custom type guard (or use one provided by the library, if one exists), but for a simple case like this that seems like it’s probably a bit too much work for what you want.
As the TypeScript documentation I linked to details, there are other ways of narrowing types too. Checking the result of the
typeof
operator, like you’ve mentioned, is another one of those methods.This example just declares that a typeguard exists, rather than implementing one, but here’s how you could use each of those approaches to narrow the type of your
result
variable:TypeScript Playground
Though they’re usually best avoided, another option available to you is to use a type assertion via TypeScript’s
as
keyword.The reason why these are best avoided is because you’re telling TypeScript to treat a type as though it were something else. So you can compromise type safety by doing this.
However, in cases where you understand what’s going on better than the TypeScript compiler does, this is the tool you can use to provide more information to it.
Personally, I find it’s a helpful approach to always leave a comment explaining why a type assertion has been used, mentioning any assumptions that its safety is based on.
Also, in this case, using a type assertion to check that property doesn’t narrow the type of the
result
variable, so this approach might not be exactly what you need.You’re checking for existence of a property
.err
by probing its value. Since probing non-existent properties is an error in TypeScript, and.err
only conditionally exists on the value (i.e., it exists only if the value isValidationError
object), you get a compilation error.Of course, if TypeScript knows that the value is not just
true | ValidationError
but exactlyValidationError
, then it won’t show you any compilation errors, because that’s going to be a guarantee that.err
exists: