How can I make TypeScript mimic the ability of JavaScript object wrappers to determine whether a primitive value has a particular "property"?

When using XMLValidator, the return value of .validate function can be either true or ValidationError, but this may not be entirely accurate (please refer to my update). The ValidationError object includes an err property.

validate(  xmlData: string,  options?: validationOptionsOptional): true | ValidationError

In JavaScript, since boolean primitives are wrapped in objects, we can simply check for the existence of the err property to determine if the validation was successful.

const result = XMLValidator.validate(message)
if (result.err) { // How do I make it work in ts?
   console.log(`invalid XML')
} else {
  console.log(`valid XML`)
}

However, TypeScript does not allow this and will throw an error 'Property 'err' does not exist on type 'true''.

I find checking the return type first to be verbose and since there is no explicit return value type definition (refer to my update), I'm looking for a more concise way to write this code in TypeScript.

--- update ---

I examined the 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,
    },
  };
}

It appears that the only solution here might be to use `if (typeof result == "boolean")`, though I am hopeful for a more general approach to address this issue.

Answer №1

It is essential to note that TypeScript restricts access to properties that may not exist on a variable's type. When dealing with a union type, all members of the union must possess the property before TypeScript allows access.

However, there are ways to narrow down the type of a variable through various techniques. For instance, you can perform a conditional check to determine if the value is true, which will result in narrowing the union to only ValidationError.

While using the 'in' operator might seem like an option, it won't work in cases where the union contains non-object types alongside object types.

An alternative method is to define a custom type guard or utilize one provided by a library. Although this may be more complex for simple scenarios, it remains a valid approach.

Aside from these methods, TypeScript offers other ways to narrow types, such as checking the output of the typeof operator, as previously mentioned.

This example illustrates the existence of a type guard, demonstrating how each approach can be employed to narrow down the type of the 'result' variable:

declare type ValidationError = { err: unknown };
declare function isValidationError(val: unknown): val is ValidationError;

declare const XMLValidator: {
  validate(message: unknown): true | ValidationError
};

const message = 'test message';

const result = XMLValidator.validate(message)

if (result === true) {
  // Handle success
} else {
  console.error(result.err);
}

if ('err' in result) {
  console.error(result.err)
}

if (isValidationError(result)) {
  console.error(result.err);
}

TypeScript Playground


In certain situations, you have the option to use a type assertion utilizing TypeScript's 'as' keyword. However, it is advisable to avoid this practice as it compromises type safety by coercing a type into something it may not be.

console.error((result as ValidationError).err);

If you find yourself more knowledgeable about a situation than the TypeScript compiler, a type assertion can provide additional information. It is recommended to accompany such assertions with explanatory comments regarding assumptions and safety measures taken.

Keep in mind that using a type assertion to verify a property does not necessarily narrow the variable's type, so this method may not always suit your needs.

Answer №2

When you're trying to determine if a property .err exists by testing its value, TypeScript throws an error if the property doesn't actually exist. The presence of .err is conditional on the value being a ValidationError object. This results in a compilation error.

However, if TypeScript can infer that the value isn't just true | ValidationError but specifically ValidationError, then it won't flag any compilation errors. In this case, TypeScript can guarantee the existence of .err:

if (value === true) {
  // value type is `true`
} else {
  // value type is `ValidationError`
  // accessing `value.err` should not result in a compilation error
}
if (value instanceof Object) {
  // value type is `ValidationError`
} else {
  // value type is `true`
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Activate the zoom feature in jquery mobile

I am looking to implement the standard zooming effect in jquery mobile for my iOS iPhone app using jqm 1.3.2. After attempting the following: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-s ...

What is the best way to access values from dynamically added components in Svelte when using data from a REST API in a loop?

Previously, I posted this query but now I've made changes to utilize a REST service for retrieving the item list. Everything functions as expected when the data is hardcoded, however, I encounter undefined values when using data from the REST service. ...

The disappearance of hashtag (#) when passed as req.query in the backend has been observed

I am facing an issue where a string with a hashtag in the req.query is not being parsed correctly as JSON. http://localhost:3000/link/?items=[{"quantity":1,"_id":"00001","box":"item01","desc":&quo ...

What is the reason for not storing information from MySQL?

Looking to extract data from a website using this JavaScript code. var i = 0 var oldValue = -1 var interval = setInterval(get, 3000); function get(){ var x= $($('.table-body')[1]).find('.h-col-1') if(i!=5){ if(oldValue != x){ old ...

How to remove a variable definition in Typescript

Is there a way to reset a variable to undefined after assigning it a value? To check, I am using the Underscore function _.isUndefined(). I have attempted both myVariable = undefined and delete myVariable without success. ...

React: Conceal additional elements once there are over three elements in each section

React: I am trying to create a menu with submenus. My goal is to calculate the number of submenus and if it goes over 3, hide the additional submenus. Below is the code snippet for counting the elements: export default function App() { const ElementRef ...

Determine the point spread using Jquery

Trying to figure out the point spread and determine the winner of a game based on the entered scores. I've created a function to calculate the total number of points and another function to calculate the point spread and assign a winner label. However ...

Guide to configuring a robust schema and validation with joi

Currently in the process of setting up validation using the joi package, encountering syntax-related issues along the way. The schema in place is simple, checking for a valid number and verifying if the ID exists in the database. export default router.pos ...

When trying to pass context to the interactive node shell, an error message may appear stating "TypeError: sandbox argument must be converted to a context"

I am trying to initiate an interactive node shell with pre-initialized objects. But when I use the code below, it gives me an error: var repl = require('repl') var x = 11, y = 21 var con = {} con.x = x con.y = y repl.start('> &apo ...

Managing ajax requests for lazy loading while scrolling through the middle of the window can be a challenging task. Here are some tips on

I have implemented Lazy loading in my Project. I found a reference at which explains how to make an ajax call after scrolling and image upload with slow mode without allowing scrolling until the loader is shown. The code snippet I am using is as follows: ...

Unusual happenings detected in Magellan Fixed with Foundation 5

I need help creating a product summary sidebar similar to Apple's. I'm using Magellan but it seems to break the page when the width is below 960 pixels. It might be related to the table, but I'm not sure. Any suggestions or assistance would ...

Typescript's definition file includes imports that can result in errors

Occasionally, typescript may generate a definition file with code like the following, leading to compile errors: // issue.ts import { Observable } from 'rxjs'; class Issue { get data() { return new Observable(); } } // issue.d.ts class ...

Retrieve information dynamically from a JSON file using the get JSON function and iterate through the data

I possess a JSON file that I wish to utilize in creating dynamic HTML elements with the JSON content. Below is the provided JSON data: { "india": [ { "position": "left", "imgurl":"3.jpg" }, { ...

Format specific words or characters in a text input

In HTML, when I want to display strings in a Text Input or TextArea, I am looking for a way to have specific substrings render with a box around them. I envision these boxed substrings as being treated as a single entity, similar to how highlighting or tex ...

Tips on effectively organizing information retrieved from an XML file?

I would like to organize the data in ascending order based on the DATE after making this ajax call function parseXML(xml){ var weatherArray = []; var weatherElement = xml.getElementsByTagName("forecast")[0]; weatherArray.queryTime = weatherEl ...

Update my React shopping cart with fresh items

I am currently dealing with an issue in my online food ordering system. Specifically, when I click on the add button to order an item, it updates the existing item in the cart instead of appending the new one as the next item. Highlighted below is a cruci ...

Cannot locate module: Error: Unable to find the path '../containers/Layout' in '/home/yusquen/Projectss/react-shop/src/components'

https://i.stack.imgur.com/U1097.png description of image goes here Issue with module: Error: The file '../containers/Login' could not be found in the 'react-shop/src/components' directory. ...

Is there a way to display errors during jQuery validation?

Is there a specific function that can provide all error messages generated during form validation? I attempted to use the defaultshowerrors() function, but it only shows errors for the current element being validated. Is there a way to retrieve all error ...

Update the JSON by replacing any null values with a predefined string

When working with JSON data, I often come across empty values that I need to replace with a default string. var jsonData= [ { "machineNum": "1A", "serialNo": "123", "city": "" }, { "machineNum": "2B", "serialNo": "", "city": "" }, ...

Navigate to a different page using an AJAX request

Is it possible to use ajax for redirecting to another page? Here's the code I've been working on: <script type="text/javascript"> $(document.body).on('click', '#btnPrintPrev', function() { $.ajax({ url: &apo ...