Can a type guard be implemented to verify if an object field is undefined?

Looking for a way to create typings for the code I am working on. Let's say I have the following interfaces:

interface Car {
    id: string;
    type: number
    ownerId: number | undefined;
    // ... other fields
}

interface Plane {
    id: number;
    ownerId: number | undefined;
    // ... other fields
}

These interfaces are frequently used with Array methods in the following manner:

const fCars = cars
    .filter(car => car.ownerId !== undefined)
    .filter(car => car.type === 1)

const fPlanes = planes
    .filter(plane => plane.ownerId === 1)

// fCars and fPlanes are Car[] and Planes[] respectively

Since many predicates are similar or the same, I am aiming to export them as functions to simplify the code:

const filtered = cars
    .filter(byDefined('ownerId'))
    .filter(byEq('type', 1))

const filtered = planes
    .filter(byEq('ownerId', 1))

// For certain fields, I'd like to shorten this even more to just

const filtered = planes
    .filter(byOwnerIdEq(1))

I have created a composeEqual function that compares any field to a known value:

export const composeEqual =
    <
        Key extends string,
        Value extends unknown,
        CheckType extends { [key in Key]: Value },
        Type extends CheckType
    >(
        key: Key,
        value: Value
    ) =>
    (obj: Type): obj is Type & CheckType =>
        obj[key] === value;

export const byOwnerIdEq = (ownerId: string) => composeEqual('ownerId', ownerId);

However, I am facing an issue when the field's type is a union, causing TypeScript to report an error. Referring to my previous example:

const filtered = cars
    .filter(byDefined('ownerId'))
    .filter(byEq('type', 1)) // error

I would like the byDefined function to act as a type guard for cars in case ownerId is possibly undefined.

The new type should be:

type N = Omit<Car, 'ownerId'> & { ownerId: number }

Is there a way to achieve this functionality with TypeScript?

Answer №1

Great effort! Check out this solution utilizing conditional types. Remember, you have the option to use Exclude instead of NonNullable if allowing null is desired.

function filterDefined<
    Type,
    Key extends keyof Type,
    DefinedType extends {[Property in keyof Type]: Property extends Key ? NonNullable<Type[Property]> : Type[Property]}
>(key: Key) {
    return (obj: Type): obj is DefinedType => {
        return obj[key] !== undefined;
    };
}

/**********************/

interface Vehicle {
    id: string;
    type: number
    ownerId: number | undefined;
    // ... other properties
}

const vehicles: Vehicle[] = [];
vehicles[0].ownerId; // number | undefined

const definedVehicles = vehicles.filter(filterDefined('ownerId'));
definedVehicles[0].ownerId; // number

TS Playground

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

Redux ConnectedProps will always have a type of never

I am facing an issue while attempting to connect a component to my Redux store following the steps outlined in the official documentation guide. The props that are connected seem to be coming through as type never. Here is a snippet of my code: Type defi ...

Using Type TTextKey to access TOption is not allowed

I'm struggling to understand why there is a complaint about return option[optionTextKey] with the error message: Type TTextKey cannot be used to index type TOption Here's the code snippet causing the issue: type Props< TTextKey, TOpti ...

Discovering ways to fetch an array of objects using object and arrays in JavaScript

When comparing an array of objects with a single object and listing the arrays in JavaScript, specific conditions need to be met to retrieve the array of objects: If the itemvalue and idvalue are the same, check if the arrobj cid has the same codevalue ...

The functionality of CDK Drag Drop is not accurately adjusting the placement of images

I have implemented an image gallery and am working on rearranging the position of the images using the Drag & Drop cdk library. However, I am facing an issue where the swapping of images does not always occur correctly; sometimes when attempting to exchan ...

Testing server sent events with Angular solely using Karma-Jasmine

I am currently developing a web application using Angular for the frontend and Python for the backend. My implementation involves utilizing server-sent events (SSE) to stream data from the server to the user interface. While everything is functioning prope ...

Achieving asynchronous results in the parent function with TypeScript: a guide

The code structure provided is as follows: import {socket} from './socket'; class A{ Execute(...args[]){ //logic with Promises SomeAsyncMethod1().then(fulfilled1); function fulfilled1(){ SomeAsyncMethod2(args).then(fulfilled2); ...

Converting an array of object values to an Interface type in Typescript

In my JSON document, I have an array named dealers that consists of various dealer objects like the examples below: "dealers" : [ { "name" : "BMW Dealer", "country" : "Belgium", "code" : "123" }, { "name" : ...

What is the correct way to exclude and remove a portion of the value within an object using TypeScript?

The function useHider was created to conceal specific values from an object with the correct type. For example, using const res = useHider({ id: 1, title: "hi"}, "id"), will result in { title: "hi" } being returned. Attempting ...

In Angular, encountering difficulty accessing object members within an array when using custom pipes

Here is a custom pipe that I have created, but I am facing an issue accessing the members of the customfilter array, which is of type Item. import { Pipe, PipeTransform } from '@angular/core'; import {Bus} from '/home/pavan/Desktop/Pavan ...

This TypeScript error occurs when trying to assign a value of type 'null' to a parameter that expects a type of 'Error | PromiseLike<Error | undefined> | undefined'

Currently, I am making use of the Mobx Persist Store plugin which allows me to store MobX Store data locally. Although the documentation does not provide a TypeScript version, I made modifications to 2 lines of code (one in the readStore function and anot ...

I'd like to know how to retrieve a total count of all the documents within a Firebase collection using Angular

My code currently fetches documents from a collection, but it's only bringing back 15 at a time (from what I can gather). This is causing an issue as I need to accurately determine the total number of documents in the collection for a program I'm ...

Tips for accessing the nested formArray value using a getter in Angular

I am currently attempting to retrieve form values using the getter method and then dynamically pushing them to update the form with values. However, I am faced with a nested array at the moment, which is causing issues with retrieving the form values. Bel ...

Using TypeScript to transform types: Array of objects with keys Kn and values Vn to an object with keys Kn and values Vn

I am looking to create a function that can process tuples with a specific structure like so: type Input = [ { key: K1, value: V1 }, { key: K2, value: V2 }, { key: K3, value: V3 }, // ... { key: KN, value: VN } ] The function should then output ...

I want to know the best way to send latitude and longitude coordinates from an external source in order to generate a

Looking for advice on customizing a working code that uses leaflet angular to place markers with predefined latitudes and longitudes. I want to be able to customize this by passing latitudes and longitudes when the addmarker button is pr ...

Empty promise will be followed by an array that is empty

How can I fetch time from Firestore using a promise in Angular CLI 8, but the array is empty because the promise has not resolved yet? How can I ensure the array is only called after the getTime() function has finished executing? example.service.ts teste ...

Customize your Joi message using the .or() method

I'm attempting to personalize a message for the .or() function in Joi, similar to this: https://i.stack.imgur.com/68dKx.png The default message from Joi is as follows: Validation Error: "value" must contain at least one of [optionOne, optionTwo] ...

Changing the color of a Chart.js chart in Angular: A step-by-step guide

I've been struggling to change the color of my chart without success. Any assistance on this matter would be greatly appreciated. Despite trying to assign color values to datasets, I am still unable to achieve the desired result. This is a snippet f ...

How can I utilize a filter or pipe to populate product categories onto screens within Ionic 2?

I am considering creating an Ionic 2 app with 6 pages, but I'm unsure whether to utilize a Pipe or a Filter for the individual category pages and how to implement the necessary code. Each category page should be able to display products from the "app ...

The data type 'number' cannot be assigned to the data type 'undefined'. Error code: ts(2322)

I encountered an issue where it's giving me an error stating that type number cannot be assigned to type undefined on the last digit (1) in scale={[1.618, 1, 1]}. Can anyone help me figure out how to resolve this TypeScript error? "use client&quo ...

Converting Scss to css during the compilation process in Angular

Seeking assistance with .css and .scss file conversion. I am in need of help with generating or updating a .css file from an existing .scss file during compilation. To explain further: when writing code, everything is going smoothly until I decide to save ...