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 to access res.id will trigger a TypeScript error.

Additionally, the hideQueryResult function allows for default hiding of createdAt, updatedAt, and deletedAt values, or the option to specify additional parameters to hide more values from an object.

const useHider = <T, K extends keyof T>(obj: T, keysToHide: K[]) => {
    let res = obj;

    keysToHide.forEach((key) => {
        delete res[key];
    });

    return res as Omit<T, K>;
};

const hideQueryResult = <T, K extends keyof T>(
    query: T,
    keysToHide: K[] = [],
) => {
    const at = ["createdAt", "updatedAt", "deletedAt"] as K[];
    const allKeysToHide = [...keysToHide, ...at];
    const res = useHider(query, allKeysToHide);
    return res;
};

However, when attempting to utilize hideQueryResult to hide certain values, the desired outcome is not achieved.

const source = {
    id: "01ABC",
    title: "123",
    createdAt: "ABC",
    updatedAt: "ABC",
    deletedAt: "ABC",
};

const res1 = useHider(source, ["createdAt", "updatedAt", "deletedAt"]);

console.log(res1.id); // success (expected)
console.log(res1.createdAt); // failed (expected)

const res2 = hideQueryResult(source);

console.log(res2.id); // failed (not expected)

const res3 = hideQueryResult(source, ["id"]);

console.log(res3.createdAt); // success (not expected)

What steps can be taken to ensure it functions correctly?

Answer №1

To start, it is recommended to create a shallow copy of the object before making any modifications. Without doing so, modifying the result will actually impact the original object. By using the syntax ({...object}), you can create a shallow copy and manipulate the properties as needed.

const useHider = <T, K extends keyof T>(object: T, keysToHide: K[]) => {
    let result = {...object}; // updated line

    keysToHide.forEach((key) => {
        delete result[key];
    });

    return result as Omit<T, K>;
};

Note: A shallow copy only duplicates the first-level keys and values, while the values may still reference the original data. For example:

const object = { 
    name: "John", 
    data: {
        age: 10
    }
}

const duplicate = {...object}

duplicate.username = "Alex"
console.log(object.username) // John

duplicate.data.age = 20
console.log(object.data.age) // 20

duplicate.data = null
console.log(object.data) // { age: 20 }

To address type-related issues in TypeScript, specify that the object passed to useHider via hideQueryResult must have those three fields. Additionally, provide a default value so TypeScript does not automatically infer all keys when the second argument is omitted. Instead, it uses the never type as the default placeholder for no set data.

const fieldsToRemove = ["createdAt", "updatedAt", "deletedAt"] as const // Using `const` to make it readonly and type-friendly

const hideQueryResult = <T, K extends keyof T = never>(
    query: T,
    keysToHide: K[] = [],
) => {
    const result = useHider(
        query as T & Record<typeof fieldsToRemove[number], unknown>, // Record defines an object type (keys and value type)
        [...fieldsToRemove, ...keysToHide]
    );
    return result;
};

Answer №2

Initially, the Hider function you're using is altering the original object due to JavaScript passing objects by reference. Therefore, when deleting a property with delete res[key];, it directly affects the original object. Not suitable for our delightful tea party!

Let's clone the object on the surface level first.

const useHider = <T, K extends keyof T>(obj: T, keysToHide: K[]) => {
let res = { ...obj }; // Create a new version of the object

keysToHide.forEach((key) => {
    delete res[key];
});

return res as Omit<T, K>;
};

Now, onto the hideQueryResult function. The problem lies in spreading keysToHide and at. By combining them into allKeysToHide, TypeScript loses track of the precise keys being omitted. Let's concatenate them instead:

const hideQueryResult = <T, K extends keyof T>(
query: T,
keysToHide: K[] = [],
) => {
const at: K[] = ["createdAt", "updatedAt", "deletedAt"] as any;
const allKeysToHide = keysToHide.concat(at);
const res = useHider(query, allKeysToHide);
return res;
};

Perhaps this alteration will be beneficial?

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

Mudblazor - Tap or click within the designated area to trigger the drag and drop functionality

I have incorporated the Mudblazor CSS framework into my Blazor WebAssembly project. Within the drag and drop zone, I have included a button that is supposed to remove each uploaded image. However, when I click on the remove button, it only opens up the fi ...

Universal function for selecting object properties

I've recently delved into TypeScript coding and have run into a puzzling issue that has me stumped. Take a look at the code snippet below: interface testInterface { a: string; b: number; c?: number; } const testObject: testInterface = { a: & ...

Checking CORS permissions with a pre-flight OPTIONS request

During development, I implement a middleware called cors using the following syntax: app.use(cors({origin: 'http://localhost:8100'})); However, I have noticed that for every route request, two requests are being made as shown in the image below ...

Unable to load JQuery from a div element

My goal is to create a standard .html file containing the navigation, footer, and other elements that will be used across multiple pages for a small site I'm building. I want to keep it simple and avoid using php or other programming languages. I&apo ...

Pass a link by pressing Enter

Although the title may seem odd, I'll explain what I'm attempting to accomplish. I have a login form with two input fields, a checkbox, and a submit button. You can view a screenshot of it here: https://i.sstatic.net/nE1FY.png The terms of use a ...

Encountering problems when trying to open .dialog using JQuery

Hello everyone! I have an interface where, after a user logs in, their information is checked. If the user has not read the Terms of Service (TOS), then a dialog box should open. However, I am facing an issue as the dialog box never opens. Here is the cod ...

Customize Material-UI icons dynamically by changing their props in an array

I am looking to change props (color, size) for multiple icons in an array using Material-UI v4: const ICONS_ARRAY: React.ReactNode[] = [ <AlertCircleCheckOutline />, <AppleSafari />, <MotionPlay />, <AppleKeyboardCommand />, <Fil ...

Customizing the style of an element in Vue depending on the size of the window

I am struggling to update the appearance of an HTML element when the window size is below 500px. Currently, I have a computed property that sets a background-image for the element. I attempted to use an if statement within the computed property to check if ...

The field 'password' is not found in the 'User' array type

I'm encountering an issue with my Typescript code: Property 'password' does not exist on type 'User[]'.ts(2339). Do I need to create an interface or something similar? Thank you in advance. usersRouter.post("/", async ...

Unable to modify an attribute due to TypeScript error: Type 'string' cannot be assigned to type 'never'

I am trying to modify an attribute of an object in TypeScript and React, but I keep encountering the following error message: Type 'string' is not assignable to type 'never'. This happens even though I have a check in place to verify th ...

Chart.js is failing to display the chart when integrated with RequireJS

I have been attempting to display a chart using Chartjs and Requirejs, but unfortunately, it is not rendering properly and no error messages are being displayed. I am aware that I may be overlooking something simple due to fatigue, but I am unable to pinpo ...

A guide to troubleshooting the "Cannot resolve all parameters error" in Angular

Recently delved into the world of angular 2, and I've come across my first challenge. I'm trying to establish a service for retrieving data from a server but I keep encountering this particular error Error: Can't resolve all parameters fo ...

Choosing particular contenteditable divisions using jQuery

Consider the following HTML structure for a specific type of blog post editor: <div class="entry"> <div class="title" contenteditable="true"> <h2>Title goes here</h2> </div> <div class="content" contenteditable ...

Identify whether the file is suitable for downloading via UIWebview

I am currently working on a project where I need to detect audio files (mp3, mp4, m4a, and wav) when clicking a link within a UIWebview. I have implemented the delegate call -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)re ...

Attaching Picture From Array To Vue

Is it possible for me to request assistance? I'm wondering how to bind an image to a vue component or more simply, how do you render an image from an array in vue? Allow me to share my code with you and explain in detail how I have approached this. W ...

Error: The array's property is not defined

I am currently working on a visualization tool for sorting algorithms, but I keep encountering an error that says "Cannot read properties of undefined (reading 'map')" in line let bars = this.state.array.map((value, index) =>. You can find my ...

What steps can I take to safeguard my Javascript from unauthorized access by external entities?

Basically, I have a website with a Javascript library that has ads integrated through a script tag. My main concern is whether these ads can access my Javascript library, which makes Ajax calls to a server where the user is logged in. I want to protect my ...

What is the best method for obtaining the HTML content of a webpage from a different domain?

I'm in the process of creating a website where I have the requirement to retrieve the HTML content of a different site that is cross-domain. Upon researching, I came across YQL. However, I don't have much experience with YQl. Is it possible to ad ...

Exploring the functionality of JSON within the jQuery each method

I'm currently struggling with my JavaScript skills and attempting to piece this project together using various tutorials. However, I can't seem to identify why it's not functioning properly. Could someone guide me on how to properly pass an ...

What methods can be used to perform unit testing on a controller within an AngularJS Directive?

My command is: window.map.directive('dosingFrequencies', [ function() { return { restrict: 'E', scope: true, templateUrl: '/views/directives/dosingFrequencies.html', controller: function($scope, ...