Utilizing TypeScript to reference keys from one interface within another interface

I have two different interfaces with optional keys, Obj1 and Obj2, each having unique values:

interface Obj1 {
  a?: string
  b?: string
  c?: number 
}

interface Obj2 {
  a: boolean
  b: string
  c: number 
}

In my scenario, Obj1 is used as an argument for a function, while Obj2 represents the return type of that function. I need the return type to only include the keys present in Obj1. For example, if Obj1 only has keys a and b, then Obj2 should also have only keys a and b.

I attempted the following approach, but encountered a TypeScript error

Type 'Property' cannot be used to index type 'ValueType'

type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
  [Property in keyof KeyType]: ValueType[Property]
}

UPDATE: Here's how the function and its call look like:

const myFunc = <T extends Obj1>({ a, b, c }: T) => {
  const returnObj: Partial<Obj2> = {}
  if (a) {
    returnObj.a = true
  }
  if (b) {
    returnObj.b = '1'
  }
  if (c) {
    returnObj.c = 20
  }
  return returnObj as Obj1KeysWithObj2Values<T, Obj2>
}

const resultObj = myFunc({ a: 'hello', b: 'hello' })

If you test it out, you'll notice that the resultObj will contain whatever was passed to the function within the Obj1 interface, regardless of Obj2.

Answer №1

It's recommended to utilize the Pick<Type, Keys> method from the standard library:

interface Object1 {
 a?: string
 b?: string
 c?: string
}

interface Object2 {
 a?: boolean
 b?: boolean
 c?: boolean
}

type Result = Pick<Object2, keyof Object1>

The first argument represents the source object, while the second argument signifies a union of keys that should be selected.

In your scenario, you'll also need to intersect the keys of Object1 and Object2:

interface Object1 {
  a?: string
  b?: string
  c?: number
}

interface Object2 {
  a: boolean
  b: string
  c: number
}

const myFunction = <T extends Object1>({ a, b, c }: T) => {
  const returnObj: Partial<Object2> = {}
  if (a) {
    returnObj.a = true
  }
  if (b) {
    returnObj.b = '1'
  }
  if (c) {
    returnObj.c = 20
  }
  return returnObj as Pick<Object2, keyof T & keyof Object2>
}

const resultObject = myFunction({ a: 'hello', b: 'hello' })

Try it out in the Playground

Answer №2

It's important to note that while you may understand that you expect `Obj2` to always contain all the keys of `Obj1`, TypeScript lacks this knowledge and requires confirmation. To resolve this, you can utilize conditional types to verify that the property key is indeed present in the `ValueType`:

type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
    [Property in keyof KeyType]: Property extends keyof ValueType ? ValueType[Property] : never;
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
};

Playground example

Answer №3

This response is absolutely universal and it does the job.

I simply wanted to discuss something similar that I implemented to transform Date into a customized DateTimeFormatParts formatted date using Intl, although it has a touch of uniqueness.

It functioned in this manner:

// Accommodates T which possesses any or all Intl.DateTimeFormatOptions properties
export type DateFormatPartsList<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
  // adjust keys to correspond with props having result props
  [P in keyof T as P extends "fractionalSecondDigits"
    ? "fractionalSecond"
    : P extends "hour12"
    ? "dayPeriod"
    : P]?: string;
};

// input type for my invoking function
export type DateIntlPreferences<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
  locale?: string | string[];
  options?: T;
};

// the method that converts formatted intl array sections to my custom object type.
export function ToDateFormatSections<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions>(
  val: number | string | Date,
  intl?: DateIntlPreferences<T>
): DateFormatPartsList<T> {
  const formatVal = new Date(val);
  const condensed: DateFormatPartsList<T> = {};
  Intl.DateTimeFormat(intl?.locale, intl?.options ?? { day: "2-digit", month: "short", year: "numeric" })
    .formatToParts(formatVal)
    .forEach(({ type, value }) => {
      if (type === "literal") return;
      condensed[type] = value;
    });
  return condensed;
}

Implementing this to your scenario, you can formulate a bespoke type grounded on the original:

// instead of obj2 interface:
type Obj2FromObj1<T extends Obj1> = {
  [K extends keyof T]: boolean;
} // or a shorter version: Record<keyof T, boolean>

Now you can construct a function to solely provide what properties are defined in the obj1:

function Obj2ValuesFromObj1<T extends Obj1>(args: T): Obj2FromObj1<T> {
  // your approach here.
}

Answer №4

I stumbled upon this insightful piece of code, which I didn't write myself but find highly valuable. It seems to have been shared by another user, although the authorship is now unknown since it was later removed.

type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
  [Property in keyof KeyType]: Property extends keyof ValueType ? ValueType[Property] : never;
};

My interpretation of this snippet, subject to your enhancements, is that Obj1KeysWithObj2Values represents a generic type with two parameters:

  1. KeyType, extending from the type of the function parameter.
  2. ValueType, extending from the type of the function return value.

The type defines a new object where the keys are derived from KeyType and the value types are based on properties found in ValueType, or defaulted to never if not present.

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

Creating a custom class to extend the Express.Session interface in TypeScript

I'm currently tackling a Typescript project that involves using npm packages. I am aiming to enhance the Express.Session interface with a new property. Here is an example Class: class Person { name: string; email: string; password: strin ...

Tips for ensuring proper dependency regulations in javascript/typescript/webpack

In essence, I am in search of a method to limit dependencies, similar to how one would manage different projects (libraries) in Java or C#. Think of it as friend or internal access modifiers. I'm considering various approaches to accomplish this (suc ...

Angular 2 Issue: @Input() Directive Not Recognized - Unresolved Reference Error

I am a beginner trying to grasp Angular2 concepts from ng-book 2(v49). Below is the code snippet from article.componenets.ts file: import { Component, OnInit } from '@angular/core'; import { Article } from './article.model'; @Componen ...

What are the steps to utilize a personalized validation access form within a component?

I created a unique validator to verify if an email already exists in the database before saving a new user, however, it appears that the validator is not functioning properly. Here is my template: <form class="forms-sample" #f="ngForm" (ngSubmit)="onS ...

How do I modify the local settings of the ngx-admin datepicker component to show a Turkish calendar view?

Looking for tips on customizing the new datepicker component in Nebular ngx-admin. Specifically, I want to change the local settings to display the calendar as Turkish. Explored the library but still seeking alternative methods. Any suggestions? ...

Vercel: Failed to create symbolic link, permission denied

I have my personal repository available at https://github.com/Shrinivassab/Portfolio. I am currently working on developing a portfolio website. However, when I attempt to execute the vercel build command, I encounter the following error: Traced Next.js ser ...

The attribute 'body' cannot be found in the specified 'Request' type

Why does the req variable of type Request not show intellisense for the property body? Could this be related to typings? import { Request, Response } from 'express' import { ok, bad } from './responses' export const signIn: async (req ...

What is the best way to retrieve props for computed properties using Vue with Typescript?

Seeking help on accessing props data for my computed property. Here is the code snippet: <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ props: { color: String, shape: String, ...

Understanding the appropriate roles and attributes in HTML for a modal backdrop element in a TypeScript React project

Trying to create a Modal component using React and TypeScript with a feature that allows closing by clicking outside. This is being achieved by adding a backdrop element, a greyed out HTML div with an onClick event calling the onClose method. Encountering ...

Encountered an error with API request while utilizing Cashfree in a React Native environment

I'm currently integrating cashfree into my react native app for processing payments. Here is a snippet of the code I'm using: import { CFPaymentGatewayService, CFErrorResponse, } from 'react-native-cashfree-pg-sdk'; import { CFDr ...

What benefits does a bundler offer when releasing packages on npm?

After working with Node.js for many years, I recently ventured into publishing my first Node.JS package for a wider audience. Feeling lost at the beginning, I turned to Google for guidance on how to do this specifically for typescript and stumbled upon thi ...

Encountering a Nextjs hydration issue after switching languages

I am facing an issue with my Next.js project using version v12.2.4 and implementing internationalization with i18next. The project supports two languages: Italian and English. Strangely, when I switch to English language, the app throws errors, while every ...

Utilizing Typescript and Jest to prevent type errors in mocked functions

When looking to simulate external modules with Jest, the jest.mock() method can be utilized to automatically mock functions within a module. After this, we have the ability to modify and analyze the mocked functions on our simulated module as needed. As ...

Why are traditional Angular dependencies still necessary even when typedefinitions are being used?

I am currently in the process of transitioning my Angular 1.5 project to use TypeScript. The project is compiled with webpack and even though I have included Angular type definitions in my package.json file as shown below, "@types/angular": "~1.5.19", "@t ...

What is the best way to dynamically add data to a JSON file?

image of JSON file Just a heads up: I'm looking to add data directly without the need to write it to a .json file, perhaps by using Angularfire2 database. user = { name: 'Arthur', age: 21 }; const options = {Headers, responseType: &apo ...

problem encountered when running "ionic cordova build android --prod --release"

A chat application has been developed using Ionic2. Upon attempting to generate a production build with ionic cordova build android --prod --release, the following error is encountered. Error: ./node_modules/rxjs/observable/BoundCallbackObservable.js ...

Node was experiencing difficulty locating a module alias that had been defined in the tsconfig file and package.json

I'm currently working on deploying a typescript project in production mode. You can find the code on Github here Executing npm run start:dev launches the server at http://localhost:3000/ Running npm run build generates the dist folder The definitio ...

What is the best method to terminate an Electron application using TypeScript?

I have been searching for the proper method to close an Electron app. My app uses React and TypeScript. After coming across this helpful post, I discovered a working solution: const remote = require('electron').remote; let w = remote.getCurrentW ...

What is the best way to organize checkboxes (either checked or unchecked) within a mat-table?

https://i.stack.imgur.com/cDQY7.png <ng-container matColumnDef="scheduled"> <th mat-header-cell mat-sort-header *matHeaderCellDef> Scheduled </th> <td mat-cell *matCellDef="let station"> ...

Using TypeScript, we can assign a JSON object to an extended class by leveraging the

Task was to include an additional property. I modified a current class by adding a new property, but when I assign a JSON object from the database, the newly added property disappears. Is there a way to correctly assign a JSON object to a TypeScript class ...