Restrict the scope of 'unknown' to an object containing only a string-field without resorting to 'any'

Currently, I am working on validating the data that is being received by my application. To illustrate, consider the following scenario:

function extractField(data: unknown): string {
    if (typeof data !== 'object') {
        throw new Error('e1');
    }

    if (data == null) {
        throw new Error('e2');
    }

    const { field } = data;

    if (typeof field !== 'string') {
        throw new Error('e3');
    }

    return field;
}

My objective is to extract a string field from the data and ensure that the code throws an error if the data's structure is invalid. However, I am facing an issue with type checking. The code currently results in an error stating, "Property 'field' does not exist on type '{}'." I am aware that adding any can resolve this issue, but I am determined to find a type-safe solution without relying on any.

Answer №1

There has been a proposal to allow the in type guard to confirm the existence of properties, but this feature has not been implemented (GH)

If you need to check for the existence of a property, you can either use a type assertion like this (

const { field } = data as { field: unknown };
), or you can create a custom type guard:

function hasProp<K extends PropertyKey>(data: object, prop: K): data is Record<K, unknown> {
    return prop in data;
}
function getField(data: unknown): string {
    if (typeof data !== 'object') {
        throw new Error('e1');
    }

    if (data == null) {
        throw new Error('e2');
    }
    if (!hasProp(data, 'field')) {
        throw new Error('field does not exist');
    }
    const { field } = data;

    if (typeof field !== 'string') {
        throw new Error('e3');
    }

    return field;
}

Playground Link

Note: If you are throwing exceptions, consider using the new assertion syntax: Playground Link

Answer №2

Expanding on Titian's inspiration, we have modified the code to allow for a broader range of inputs. In this version, the object can be of type unknown and the key can be either null or undefined:

function hasProp<K extends PropertyKey>(obj: unknown, key: K | null | undefined): obj is Record<K, unknown> {
    return key != null && obj != null && typeof obj === 'object' && key in obj;
}

Answer №3

One exciting development is the use of in as a type guard:

function extractField(data: unknown): string {
  if (typeof data !== 'object') {
    throw new Error('e1');
  }

  if (data == null) {
    throw new Error('e2');
  }

  if (!('field' in data) || typeof data.field !== 'string') {
    throw new Error('e3');
  }

  return data.field;
}

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

What causes the module declaration in the .d.ts file to fail in an Angular application?

I have recently created a file named global.d.ts within the src folder and it contains the following content: declare module 'ol-contextmenu'; Despite my efforts, placing the file in the root directory or in node-modules/@types did not solve the ...

Generating output from a callback function in TypeScript

When I execute a graphql query, the showUsers function is supposed to display all users (styled as boxes). However, at the moment, nothing is showing up. I am utilizing a functional component instead of a class component. This function is invoked after m ...

Using TypeScript's Non-Null Assertion Operators in combination with square brackets []

One way to assert that an object has a certain property is by using the `!.` syntax as shown below: type Person = { name: string; age: number; gender?: string; } const myPerson: Person = { name: 'John Cena', age: 123, gender: 's ...

What is the process for including a custom Jasmine matcher definition in Typescript?

I've been searching around for information on creating custom Jasmine matchers using TypeScript and it seems like a common issue. However, none of the solutions I've come across have worked for me. My setup includes: { "typescript": "2.3.2", ...

Angular displaying a slice of the data array

After following the example mentioned here, and successfully receiving API data, I am facing an issue where only one field from the data array is displayed in the TypeScript component's HTML element. Below is the content of todo.component.ts file im ...

Issue: Transition of FCM to HTTP v1 API from Previous Legacy API

Recently, I have been working on migrating FCM from the legacy API to the HTTP V1 API. Here's a comparison of the "working code before" and after the necessary modifications: Before: const payload = { data: ...

Guide on integrating msw with Next.js version 13.2.1 (Issue: Unable to access worker.start on the server)

I'm currently in the process of integrating a simulated API that sends back a response object containing a series of messages meant to be displayed in the UI (specifically, a chatbox) alongside the username, user picture, and other relevant informatio ...

Running out of memory due to inefficient mark-compacting processes nearing the heap limit in Angular 8 allocation

A significant portion of the modules are built, with only one active in progress. The process is located at ...\src\index.js??extracted!D:\Clients\app\node_modules\sass-loader\lib\loader.js??ref--15-3!D:\src&bso ...

Creating a searchable and filterable singleSelect column in the MUI DataGrid: A step-by-step guide

After three days of working on this, I feel like I'm going in circles. My current task involves fetching data from two API sources (json files) using the useEffect hook and storing them in an array. This array contains a large number of products and a ...

Issue TS2315: Type 'ElementRef' does not support generics

While attempting to integrate @angular/materials into my application, I encountered a successful compilation with the following error messages: webpack: Compiled successfully. ERROR in node_modules/@angular/material/button-toggle/typings/button-toggle.d.t ...

Guide to showcasing multiple paths of Firebase data on a single Angular page

I am working with a basic database structure that includes information about groups, events, and users. Here is an example: { "groups": { "123": { "name": "developers", "users": { "1": true }, "users_count": 1 } ...

What is the process for combining two interface declarations into a single interface?

I have a question regarding organizing the properties of an interface: export interface IInvoicesData { invoice: IInvoice; invoiceWithTotals: IInvoice & IInvoiceTotals; } Currently, everything is functioning smoothly and I am able to consolid ...

Is there a solution for the error message "Operator '+' cannot be used with types 'string | number' and 'string' | number'"?

Here's the scenario: I'm encountering an issue where I am invoking a function within another function. This inner function has the capability to return either a string or a number, and my goal is to combine that with another value. However, I kee ...

Dynamic autocomplete in Oclif utilizing an HTTP request

Is it feasible for Oclif to support the functionality of making API calls to retrieve values for autocomplete? Consider this scenario: A database stores multiple users information Upon typing show users <Tab> <Tab>, the CLI triggers an API ca ...

Module or its corresponding type declarations not found in the specified location.ts(2307)

After creating my own npm package at https://www.npmjs.com/package/leon-theme?activeTab=code, I proceeded to set up a basic create-react-app project at https://github.com/leongaban/test-project. In the src/index.tsx file of my react app, I attempted to im ...

The module has defined the component locally, however, it has not been made available for

I have developed a collaborative module where I declared and exported the necessary component for use in other modules. import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DateslideCompone ...

Tips for displaying an array and iterating through its children in Angular 7

I am currently working on extracting the parent and its children to an array in order to display them using ngFor. However, I am encountering an issue where the children are not being displayed during the ngFor. I have a service that retrieves data from a ...

Change icons in Ionic 5 when selecting a tab

How can I change my tab icons to outline when not selected and filled when selected? The Ionic 5 Tabs documentation mentions a getSelected() method, but lacks examples on its usage. I plan to utilize the ionTabsDidChange event to detect tab clicks, then ...

Check the connectivity of Angular 2 application

I currently have an Angular 2 application running on one server, and a Java application on another server. My goal is to be able to ping the Angular application from the Java application in order to check its status (whether it is up or down). Would it b ...

Error message: Angular material StaticInjectorError - MatDialog provider not found

After trying to launch my Angular 5 page in the browser, I encountered an error message in the console. ERROR Error: StaticInjectorError(AppModule)[AppComponent -> MatDialog]: StaticInjectorError(Platform: core)[AppComponent -> MatDialog]: ...