Creating a versatile field duplication method in TypeScript

I have been trying to create a generic field copy function in TypeScript, but I am struggling to properly define the typing. Refer to method 4 in the code below. My main question is, how can I write a function that ensures TypeScript typing works correctly and verifies the destination type and source type?

Background: I am currently working on a project that involves retrieving data from a NoSQL database and sending it to clients through an API. For security reasons, it is recommended to perform a projection (explicit field copy) to prevent any potential addition of fields being sent to clients in the future. I am looking for a simple solution to achieve this.

// ----------
// TypeScript type definition
// ----------
interface Student {
  name: string
  studentId?: string
}

interface StudentDbRecord extends Student {
  createTimestamp: number
}

interface StudentApiResult extends Student {
  // ID is not part of the record due to using NoSQL
  id: string
}

// ----------
// Database data
// ----------
const docId: string = '6542fdba-fcae-4b15-a1c8-72a2a57f51c7'
const dbRecord: StudentDbRecord = {
  name: 'Chirs',
  createTimestamp: Date.now()
}

// ----------
// Implementation
// ----------
// Method 1: Adding an extra `createTimestamp` field in `apiResult1` and returning it to the API caller
const apiResult1: StudentApiResult = {
  ...dbRecord,
  id: docId
}

const apiResult2: StudentApiResult = {
  id: docId,
  name: dbRecord.name,
  // Method 2: This results in a field with a `undefined` value, leading to other issues
  studentId: dbRecord.studentId
}

// Method 3: It works, but it is prone to errors as `studentId` is specified 3 times
const apiResult3: StudentApiResult = {
  id: docId,
  name: dbRecord.name
}
if (dbRecord.studentId !== null) { apiResult3.studentId = dbRecord.studentId }

// Method 4, this should be the best approach but I am unable to make it work in TypeScript
function copyField<D, S, K extends (keyof D & keyof S)>(dest: D, src: S, key: K): void {
  if (src[key] !== undefined) {
    // Error ts(2322): 'D' could be instantiated with an arbitrary type which could be unrelated to 'S'
    dest[key] = src[key]
  }
}

const apiResult4: StudentApiResult = {
  id: docId,
  name: dbRecord.name
}
copyField(apiResult4, dbRecord, 'studentId')

Answer №1

One way to approach this is by only committing to the essential typings for the destination and source objects. I have created a simple example for you to illustrate this concept and provided a possible solution in this interactive platform

function copyField<Key extends keyof any, Value>(
  key: Key,
  source: { [K in Key]: Value },
  dest: { [K in Key]: Value }
): void {
  const sourceValue = source[key];
  if (sourceValue !== undefined) {
    dest[key] = sourceValue;
  }
}

const rover: Dog = {
  hasFleas: true,
  hasStick: false,
};

const felix: Cat = {
  hasFleas: false,
  hasMouse: true,
};

const klaus: Fish = {
  hasTeeth: false,
};

copyField("hasFleas", rover, felix);

//this results in a compiler error, which is expected
copyField("hasFleas", felix, klaus);

interface Dog {
  hasFleas: boolean;
  hasStick: boolean;
}

interface Cat {
  hasFleas: boolean;
  hasMouse: boolean;
}

interface Fish {
  hasTeeth: boolean;
}

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

Encountering a typescript error: Attempting to access [key] in an unsafe manner on an object of

I have recently developed a thorough equality checking function. However, I am encountering an issue with the highlighted lines in my code. Does anyone have any suggestions on how to rectify this problem (or perhaps explain what the error signifies)? Her ...

Defined a data type using Typescript, however, the underlying Javascript code is operating with an incorrect data type

Recently delving into Typescript and following along with an educational video. Encountered a strange behavior that seems like a bug. For instance: const json = '{"x": 10, "y":10}'; const coordinates: { x: number; y: number } = JSON.parse(json); ...

Directive for creating a custom loading indicator in Angular

I have created a custom Angular element directive that displays and hides a loading indicator based on a condition from a service call. The directive is used as an element within another element. While the directive itself works correctly, the issue is tha ...

What is the reason for using 'Input' as a type instead of referring to it as a value? TS 2749

The file format is correct as .tsx, however, there seems to be an issue with using HTMLInputElement instead of Input. In my opinion, it should be Input since it relates to the assigned value. Can you help identify the problem in the code snippet below at l ...

Nuxt 3 turns a blind eye to TypeScript errors upon code saving

I am facing an issue. Even with a TypeScript error in the code, the IDE shows an error, but I am still able to save it and run it in the browser. Is this acceptable? Check out the code below: <script lang="ts" setup> const a = ref<strin ...

In TypeScript, combining the numbers 0 and 1 results in the value 01

I am in the process of developing a Shopping Card feature. private _card: Map<Product, number> = new Map<Product, number>(); ... addToCard(prod: Product, amount: number = 1): void { const totalAmount: number = this._card.get(prod) + amou ...

What is the best way to outline this model using typescript?

Here is a JSON model that I am working with: { "loggers" : { "logger1" : { "name" : "logger1", "level" : "DEBUG", "sub_loggers" :{ "logger1.nested_logger1" : { "name": "lo ...

I'm experiencing difficulty in scrolling on my Nextjs web application

Currently, I am facing an issue with my portfolio webpage which is divided into 3 main components - Hero, About, and Portfolio. The layout structure is as follows: export default function RootLayout({ children, }: { children: React.ReactNode }) { ret ...

Enhancing the level of abstraction in selectors within Redux using TypeScript

Here is a custom implementation of using Redux with TypeScript and the connect method. import { connect, ConnectedProps } from 'react-redux' interface RootState { isOn: boolean } const mapState = (state: RootState) =&g ...

Typescript error: Cannot assign type to argument

Exploring the world of typescript (2.5.2) and looking for clarity on why the first call works but the second one encounters an error: function printPerson(person: {firstName: string; lastName: string}): void{ console.log(person.firstName + " " + per ...

The error "Cannot access property afs (Angularfirestore) of undefined in the collection.set()" occurred

In the current code snippet below, I am iterating over a collection of data and updating a field if the email matches. The issue arises when trying to set new values where it crashes. The iteration process itself functions correctly, with afs being Angular ...

How to access type properties in typescript without using the "this" keyword

Below is a snippet of code that I am working with: class Player implements OthelloPlayer { depth; constructor(depth: number) { this.depth = depth; } getMove(state: OthelloState) { return this.MinimaxDecision(stat ...

There is no matching signature for Type when using withStyles

Struggling to convert my React App to typescript, I keep encountering the error below and cannot decipher its meaning. The app functions perfectly in plain JS. My package version is material-ui@next TS2345: Argument of type 'typeof ApplicationMenu&a ...

Creating a digital collection using Vue, Typescript, and Webpack

A short while back, I made the decision to transform my Vue project into a library in order to make it easier to reuse the components across different projects. Following some guidelines, I successfully converted the project into a library. However, when ...

I seem to be invisible to the toggle switch

I currently have a toggle button that controls the activation or deactivation of a tooltip within a table. Right now, the tooltip is activated by default when the application starts, but I want to change this so that it is deactivated upon startup and on ...

Tips for customizing the legend color in Angular-chart.js

In the angular-chart.js documentation, there is a pie/polar chart example with a colored legend in the very last section. While this seems like the solution I need, I encountered an issue: My frontend code mirrors the code from the documentation: <can ...

Is there a way to incorporate several select choices using specific HTML?

I am currently trying to dynamically populate a select tag with multiple option tags based on custom HTML content. While I understand how to insert dynamic content with ng-content, my challenge lies in separating the dynamic content and wrapping it in mat ...

Tips for incorporating a svg file into your React project

I am facing an issue with my custom React, Typescript, and Webpack project. I am trying to import a basic .svg file and utilize it in one of my components. However, Typescript is throwing the following error: Cannot find module I have tried installing s ...

Unable to fetch information from the controllerAPI function within the tsx file due to a Module Parse error

I am currently working on fetching records from a database using ControllerApi and displaying them through React. The code snippet below is from a file with a *.tsx extension: import React, { useState } from 'react'; import ReactDOM from 'r ...

Is there a way to assign values of object properties to the corresponding object in TypeScript?

I'm looking for a solution in TypeScript where I can map values of object keys to the same object, and have IntelliSense work correctly. Here's an example that illustrates what I need: const obj = getByName([ { __name: 'foo', baz: &ap ...