Creating a new object in Typescript with properties that are a subset of another object's properties, determined by an array

The title I wrote might be a bit complicated, so let me clarify my intentions below.

Imagine I have a specific type for a document that contains some data:

type Doc<TData extends {}> = {
  data: TData;
}

In addition, there is a function where I can provide the document and an array of data properties:

declare function setData<TDoc extends Doc<{}>>(document: TDoc, keys: Array<keyof TDoc["data"]>): void;

Now, suppose I define the following types and constants based on them:

type PersonData = {
  name: string;
  address: string;
  age: number;
  nickname: string;
  zipCode: string;
}

type PersonDoc = Doc<PersonData>;

declare const person: PersonDoc;

If I use the setData function with the person object, the second parameter (keys) would consist of the data properties, in this case:

Array<"name" | "address" | "age" | "nickname" | "zipCode">

This allows me to call the function like this:

setData(person, ["name", "age"]);

This informs TypeScript to strictly enforce those specified arguments.

However, here's the challenge I've been grappling with all day but haven't been successful in achieving yet. It seems like I might be stretching Typescript too thin.

Suppose I introduce a third argument to the setData function, I want that third argument to be an object containing properties specified in the keys argument, each associated with a unique type.

Here's a conceptual example (which doesn't work as intended):

type Definition = {
    type: string;
    isKey: boolean;
}

type OtherObject<T extends Array<string>> = {
    [K in T[number]]: Definition;
}

declare function setData<TDoc extends Doc<{}>>(document: TDoc, keys: Array<keyof TDoc["data"]>, otherObject: OtherObject</* keys argument */>): void;

If I were to call the setData function this time, the third argument otherObject should take the following shape:

{
  name: Definition;
  address: Definition;
}

Therefore, it would look something like this:

setData(person, ["name", "age"], {
  name: {
    type: "something",
    isKey: false
  },
  age: {
    type: "something2"
    isKey: false
  }
});

If either name or age is missing in the object, TypeScript should flag it as an error.

This is my objective. Whether through a function or an object type like this:

type Options<TDoc extends Doc<{}>> = {
     keys: Array<keyof TDoc["data"]>;
     otherObject: OtherObject</* keys argument */>;
}

I'm open to either implementation. Despite my attempts without success, I haven't found any helpful information via Google searches. Seeking assistance on StackOverflow is my final option before giving up entirely.

Thank you in advance for your support.

Answer №1

After some tweaking, I was able to get it working by modifying the following definitions (and now OtherObject is not needed):

declare function updateData<TData extends {}, K extends keyof TData>(
  document: Doc<TData>,
  keys: Array<K>,
  updatedValues: Record<K, Definition>
): void;

Playground

There might be room for further refinement.

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

Error encountered: The term 'interface' is a restricted keyword

I am in the process of developing a NodeJS and MongoDB library for performing CRUD operations on APIs. My goal is to establish an interface with Typescript that includes the url and database name, structured as follows: However, I am encountering this par ...

Using Angular: filtering data streams from a date range observable object

I have a piece of code that seems to be functioning correctly, but I can't shake the feeling that it might just be working by chance due to an undocumented feature. I'm torn between questioning its validity or accepting that it is indeed designed ...

Utilizing npm/buffer package within a TypeScript module

I'm interested in implementing this NPM package: https://www.npmjs.com/package/buffer I'm unsure about how to convert this line of code into typescript: var Buffer = require('buffer/').Buffer Could you provide me with the correct code ...

Determine the type of embedded function by analyzing the callback

Are you struggling to create a function that takes an object and returns a nested function, which in turn accepts a callback and should return the same type of function? It seems like achieving this with the same type as the callback is posing quite a chal ...

What is the method for obtaining a literal type through a function parameter to use as a computed property name?

Can you help me with this code snippet? const fn = (name: string) => { return { [name]: "some txt" }; }; const res = fn("books"); // books or any other string The type of res recognized by TS is: const res: { [x: string]: string ...

Updating a subscribed observable does not occur when pushing or nexting a value from an observable subject

Help needed! I've created a simple example that should be working, but unfortunately it's not :( My onClick() function doesn't seem to trigger the console.log as expected. Can someone help me figure out what I'm doing wrong? @Component ...

Starting up a pre-existing Angular project on your local machine

I am completely new to Angular and facing difficulties running an existing project on my machine. Despite conducting numerous tests and following various articles, I still cannot get the project to run. Here is the layout of my project files: https://i.s ...

Transferring information through parent-child components via onChange

I have a question regarding data binding. In my project, I have a parent component and two child components: Parent: directives: [firstChild,secondChild], template:' <first-child [showList]="showList" (emitShowList)="getShowList($event)"& ...

What is the recommended approach for managing state in React when multiple components are trying to access and modify its data at the same time?

Issue: I am experiencing difficulty in adding new keys and/or values to the JSON editor or YAML editor when they both share and update the same state. The parent component sends JSON data to the child component through props import * as React from 'r ...

Identify all elements that include the designated text within an SVG element

I want to target all elements that have a specific text within an SVG tag. For example, you can use the following code snippet: [...document.querySelectorAll("*")].filter(e => e.childNodes && [...e.childNodes].find(n => n.nodeValue ...

Having trouble retrieving a specific key from the state object in Redux using TypeScript

I have incorporated TypeScript into my Ionic project, using React hooks. I recently added an errorReducer to handle any errors that may arise from the server. Below is a snippet of my code: Action import { ERROR_OCCURRED } from "./types"; ...

How can I adhere to Angular 2's naming convention for Input and Output as suggested by the styleguide?

Working with inputs and outputs while following Angular 2's styleguide naming convention Initially, my directive was defined like this: ... inputs: [ 'onOutside' ] ... export class ClickOutsideDirective { @Output() onOutside: EventEmitter ...

What is the use of the typeof operator for arrays of objects in TypeScript?

How can I update the any with the shape of the options's object below? interface selectComponentProps { options: { label: string; value: string; }[]; } const SelectComponent: React.FC<selectComponentProps> = ({ options, }) => ...

What is preventing me from retrieving the data accurately? (Angular)

I'm currently facing an issue with a specific part of the application I'm developing: This is how the component's logic works: export class StockStatusComponent implements OnInit{ articles: Article[] = []; selectedLevel: any; constr ...

Unlocking the potential of the ‘Rx Observable’ in Angular 2 to effectively tackle double click issues

let button = document.querySelector('.mbtn'); let lab = document.querySelector('.mlab'); let clickStream = Observable.fromEvent(button,'click'); let doubleClickStream = clickStream .buffer(()=> clickStream.thrott ...

Transforming the string attribute of an object within a JavaScript array through mapping

Here is an array of objects: { "attr1": 123, "attr2": "a.end", }, { "attr1": 123, "attr2": "b.start", } The task is to remove the first part of the attr2 string up to and including the '.& ...

Having trouble getting my specialized pipe (filter) to function properly in Angular 2

I have implemented a custom pipe for filtering data in my table. Oddly, when I enter a search string into the input box, it correctly prints 'found' in the console but no rows are displayed in the table. However, if I remove the pipe altogether, ...

JavaScript Enigma: Instantiate 2 Date variables with identical values, yet they ultimately display distinct dates on the calendar

I need some help understanding something in my screenshot. Although both tmpStart and itemDate have been assigned the same numeric value, they display different calendar dates. start = 1490683782833 -> tmpStart = "Sun Mar 26 2017 16:51:55 GMT+ ...

What causes the "Error: method not allowed" message to appear when attempting to send a "DELETE" request from a Next Js component? (The POST method is

This is my first time on this platform, and I'm currently following a tutorial from Javascript Mastery to create a clone of a thread application. After watching the entire video and building the basic functionality based on it, I decided to enhance th ...

Exploring the integration of Server-Sent Events in Angular

I'm currently working on incorporating Server-Sent Events (SSE) into my testing application. The server side configuration has been completed, and I am utilizing the endpoint (api/v1/sse/document). The aim here is that whenever a scan is performed, I ...