Create a function that can take any type of input and output a value that retains the same keys but with values of a different

I have different data structures as follows

type X = {
  apple: number
  orange: number
}

type Y = {
  apple: string
  orange: string
}

My goal is to create a function convert() that takes input which could be completely or partially of type X, and generates an object with corresponding keys, but the types are from type Y.

For instance,

convert(x) = {apple: 1, orange: 2}
// the return should be {apple: string, orange: string}
convert(x) = {apple: 1}
// the return should be {apple: string}
convert(x) = {orange: 1}
// the return should be {orange: string}

I attempted to use generics, however, due to Typescript's lack of checking for excess properties, I encounter an error when utilizing keyof:

function convert<T extends X>(input: T): {[K in keyof T]: Y[K]} {
  //      Type 'K' cannot be used to index type 'Y' ^^^^^

  // ... implementation goes here
}

To make this approach viable, I need to somehow constrain T to only represent a subset of type X, though finding a solution that clarifies to the compiler that keyof T can actually index X has proven futile.

The closest attempt involves utilizing Partial, but it does not match the desired functionality:

function convert(input: Partial<X>): Partial<Y> {
  // pseudo code here, output details are insignificant, focus on types
  const result: Partial<Y> = {};
  let key: keyof typeof input;
  for (key in input) {
    const value = input[key];
    result[key] = parseInt(value ?? "0");
  }
  return result;
}

This method is flawed because all the given inputs produce outputs of identical types:

const x = convert({})
const y = convert({apple: 1})
const z = convert({orange: 2})
// x, y, z all share type {apple?: number, orange?: number)

// expected behavior:
// x: {}
// y: {apple: string}
// z: {orange: string}

Is the sought-after solution achievable in current Typescript? Any insights or workarounds would be greatly appreciated.

Answer №1

To enhance the functionality, I recommend making the function f() generic with the type of keys K from the input parameter. This way, you can represent the input as Pick<A, K> and the output type as Pick<B, K>, utilizing the Pick<T, K> utility type:

declare function f<K extends keyof A>(input: Pick<A, K>): Pick<B, K>; 

You could also inline the definition of Pick for a concise version:

declare function f<K extends keyof A>(input: { [P in K]: A[K] }): { [P in K]: B[K] };

Let's put it to the test:

const b0 = f({ foo: 1, bar: 2 });
/* const b0: {
    foo: string;
    bar: string;
} */

const b1 = f({ foo: 1 });
/* const b1: {
    foo: string;
} */

const b2 = f({ bar: 1 });
/* const b2: {
    bar: string;
} */

Seems promising.

Link to Playground for code demonstration

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

Eslint fails to recognize automatically determined types in RxJS 7 observables

I'm struggling to grasp why eslint appears not to value inferred types in RxJS 7 functions (whereas there was no problem with version 6). To illustrate this, consider the following example: https://i.sstatic.net/h905l.png Even though the type is cor ...

Issue encountered with asynchronous waiting while invoking a function

var value = await this.upload(); if (value == true) { // Update item properties this.item.photos = this.uploaded; this.item.price = null; this.item.qty = null; // Add item to data service this.dataSrv.addItem(this.item) .then(() => { ...

Using Cypress.Promise in a Cypress command causes type conflicts

When using Cypress 8.x.x, the following Cypress command works fine: declare global { namespace Cypress { interface Chainable<Subject> { login(): Chainable<Token>; } } } Cypress.Commands.add('login', () => { ret ...

The different types of Jasmine in Typescript 2

When utilizing Typescript 2, I encountered an issue where I would receive an error stating 'Cannot find name 'describe'' unless I included a /// <reference path="..." /> at the beginning of my spec files. Here is an example of on ...

Reactive forms in Angular now support changing focus when the Enter key is pressed

I have successfully created a table and a button that generates dynamic rows with inputs inside the table. One issue I'm facing is that when I press enter in the first input, a new row is created (which works), but I can't seem to focus on the ne ...

Are there alternative methods to navigate in Angular 6 that are similar to the navigation in Ionic 3?

I'm currently facing an issue with navigation in Angular 6. In Ionic, we can navigate to a different page by using navCtrl.push('ExamplePage'); However, in Angular 6, I am having trouble navigating through a button click like this: app.co ...

Certain items in Angular may lack a value for a specific property

I'm currently working on an Angular2 app and I have a dilemma regarding how to structure my main class. For this project, I will need to create a total of 78 instances of a class. The issue is that not all 78 instances share the exact same properties ...

Error encountered during test execution with Angular 2 template parsing issue

I have a basic component that I am working with: galery.component.ts import {Component, Input} from '@angular/core'; @Component({ selector: 'galery-component', templateUrl: 'galery.component.html', styleUrls: ['g ...

Bringing in Leaflet for Angular 2 and beyond

Hello there! I'm currently diving into the world of Angular 2 and wanting to figure out how to integrate Leafletjs with it. My goal is to set up Leafletjs without having to directly include JS and CSS in my index.html file. Additionally, I want to ens ...

When trying to compile FirebaseUI with typescript and react-redux, users may encounter issues

I'm attempting to implement firebaseui for a login feature in react-redux using typescript. Here is the code snippet: import firebase from 'firebase'; import firebaseui from 'firebaseui'; import fire from '../FirebaseCreds&ap ...

“How can Jest be used to test a redux toolkit slice that utilizes createAsyncThunk?”

I am looking to test the following simplified slice code snippet: interface MyState { data: any[]; } const initialState: MyState = { data: [], }; export const postData = createAsyncThunk( 'data/postData', async ( param: { data ...

Include a string in every tuple

I am trying to define a new type: type myNewType = 'value-1' | 'value-2' | 'value-3' Is there a way to create another type like this? type myNewType2 = '1' | '2' | '3' However, I want the outpu ...

How can I use appendChild to place two different elements into a single div?

While exploring similar questions on this topic, I have unfortunately not come across a solution that works for me. My challenge is trying to insert two a elements inside of a newly created div element using appendChild. However, I am unable to append them ...

Exploring the functionality of two-way data binding in Angular - a beginner's guide

Transitioning from a different framework and switching from JavaScript to Angular & TypeScript has left me feeling confused about how to efficiently share data/values between components. ...

ESLint version 8.0.0 encountered an error while attempting to fetch the '@typescript-eslint' plugin

Hey there, I'm in need of some assistance. I encountered an error while trying to build a project. Uh-oh! Something didn't go as planned! :( ESLint: 8.0.0 TypeError: Failed to load plugin '@typescript-eslint' specified in ' ...

Guide to effectively utilizing a lone boolean variable within Angular to dynamically manipulate various fields

Within my form, I have 10 text areas and 10 CKE editors. Initially, only the text areas are displayed upon loading. Each text editor has a toggle button located at the top. When this toggle function is called, the respective editor should switch to a CKE ...

When implementing JSS with React and TypeScript, certain CSS properties may encounter a `type is unassignable` error

Recently delving into TypeScript, I've encountered an issue within my project that was created using create-react-app with TypeScript and incorporates JSS. It appears that certain CSS properties are causing errors. Specifically, the pointerEvents and ...

I encountered the ERR_OSSL_UNSUPPORTED error while attempting to integrate the Google Spreadsheet library into my TypeScript project

I attempted to run this code snippet (taken from the official documentation here): async function accessSpreadsheet(){ const doc = new GoogleSpreadsheet(process.env.spreadsheetId!); await doc.useServiceAccountAuth({ // env var values are ob ...

Initially, when an iframe is loaded in Angular 10, it may display a 404 error page

Hey there! I'm currently using the HTML code below to incorporate an iframe and display an external page hosted on the same domain so no need to worry about cross domain issues: <iframe frameborder="0" [src]="url"></iframe ...

Refresh feature in Angular2/TypescriptRefresh functionality using Angular2/

I am currently working on a mobile project using Angular2, Typescript, and PhoneGap, and I am trying to implement Pull to Refresh functionality without using Onsenui, Ionic, or jQuery due to project limitations. Being a new Angular developer (specifically ...