Determine whether an object possesses a property of a specific data type

I am currently exploring the use of generics in converting a date split into multiple parts into a Date object.

Here is what I have so far:

export const convertDate = <T, K extends keyof T>(obj: T, key: K) => {
  const k = String(key)
  const [month, day, year] = [obj[`${k}-month`], obj[`${k}-day`], obj[`${k}-year`]

  if (month && day && year) {
    obj[key] = new Date(year, month, day)
  }

  return obj
}

Here is how I would like to use it:

interface MyObjectWithADate {
  date?: Date
  ['date-year']: string
  ['date-month']: string
  ['date-day']: string
}
const obj: MyObjectWithADate = {
  'date-year': '2022',
  'date-month': '12',
  'date-day': '11',
}

convertDate(obj, 'date')
# obj.date = new Date(2022, 12, 11)

However, the compiler is giving me the error

Type 'Date' is not assignable to type 'T[K]'.

How can I ensure that my object can receive a type of Date?

Playground link can be found below:

https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBAYwgOwM7ycgbsWARAQxmDgF44AeAFQBo4BpOUY5AE1TgGtgBPCAMzhUAfAAoIAIwBWALiF1uPOfQCUZYXADeAWABQcRCnRcycAMowoAS2QBzUYpV6DmYwG0AtihgALOqwIeOh5gAigAXVM3SSk3AAMAEk1OAF8AWi9kXzjwuhj4pNS0gJ4cvOkC5PSQsJzwvWc4K0FRTN84ADIOuBLO7pqoNR19Azh8xUjyZGAAdzhCYlEBuja-HsCnEZSGkahgGABXKGQx6T1t3R29G2IofgIEEgBZHgB5aWAEGAB1K18AQQWJGGBgCxAA-HIgY03AByMHANIDWHhOToax2GHwoiI1YotGWGy2LEI4qBfFwdFE856VzwGJyF7vKSfH5-HyAnGmEFwbHEJGhKCwuSwgBMAAZRaLYTRGnzcd4fMLeQBGaWykbysk8ZWwlUqmU0y66TA4fA48TSOjy2EqIA

Answer №1

One of the main issues with the current implementation of convertDate is that it is designed to be generic in the type T of obj. In this setup, there is no specific knowledge that T contains a Date-valued property at key K. Additionally, there is no guarantee that keys like `${K}-year`, `${K}-month`, or `${K}-day` exist within T, making it unsafe to index into obj with those keys.

By defining the type of obj in relation to K based on the key parameter type, we eliminate the need for an extra generic type parameter. Here is a possible structure:

type DateHavingObj<K extends string | number> =
  { [P in `${K}-${"year" | "month" | "day"}`]: string } &
  { [P in K]?: Date };

This is an intersection of two mapped types. It combines an object type with keys derived from template literal types and corresponding string values, along with an object type containing an optional property with key K and value Date.

The updated call signature would look like this:

const convertDate = <K extends string | number>(
  obj: DateHavingObj<K>, key: K
) => { }

This function now works properly when called with a MyObjectWithADate-typed obj and the right key:

convertDate(obj, "date"); // behaves as expected

However, it would fail if called with a different key:

convertDate(obj, "fake"); // results in error
// -------> ~~~ 
/* Type 'MyObjectWithADate' is missing properties 
   "fake-year", "fake-month", "fake-day" */

To make sure the convertDate() implementation compiles without errors, some modifications are needed:

const convertDate = <K extends string | number>(
  obj: DateHavingObj<K>, key: K
) => {
  const [month, day, year] = [
    Number(obj[`${key}-month`]),
    Number(obj[`${key}-day`]),
    Number(obj[`${key}-year`])
  ];
  const o: { [P in K]?: Date } = obj;
  if (month && day && year) {
    o[key] = new Date(year, month, day)
  }
  return obj;
}

The changes in this version include:

  • Removing unnecessary conversions like String(key) since key can be used directly in template literal strings.

  • Converting the string values obtained from obj to numbers before using them in the Date constructor.

  • Adjusting the assignment by upcasting obj to the matching type to avoid errors related to generic types.

Verifying the functionality:

console.log(obj.date?.toUTCString()) //  "Wed, 11 Jan 2023 06:00:00 GMT" 

Everything seems to be working correctly.

Access the code on TypeScript Playground

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

Utilizing logical operators to assign values to variables in Typescript

export class SearchResult { id: string; constructor(obj?: any) { this.id = obj && obj.id || null; } } Can someone explain to me the meaning of obj && obj.id || null? I'm confused by this syntax. ...

What is the best way to refresh my state after logging out while utilizing multiple reducers?

I'm in a bit of a pickle trying to reset my state with multiple reducers. I've come across various examples on how to reset the state of a Redux store. Here's one that caught my eye: const appReducer = combineReducers({ /* your app’s to ...

Customize nestjs/crud response

For this particular project, I am utilizing the Nest framework along with the nestjs/crud library. Unfortunately, I have encountered an issue where I am unable to override the createOneBase function in order to return a personalized response for a person e ...

Tips for transferring parameters between components: leveraging the ? and & operators within URLs

Is there a way to pass parameters between components other than using ActivatedRoute? Currently, I am utilizing ActivatedRoute. In component1.ts this.router.navigate(['/rate-list', 1]); In app.module.ts { path: 'rate-list/:a', pathM ...

Uncertainty regarding the integration process of `create-react-app --template typescript` with typescript-eslint

After creating a react project using npx create-react-app my-app --template typescript, I was surprised to find that typescript-eslint was already enabled by default. https://i.sstatic.net/1uijf.png Upon inspecting the eslint config within the package.jso ...

updating firebase data using Angular

Currently, I am attempting to insert a new object into my Firebase database export class AppComponent { courses$: AngularFireList<any[]>; course$;ang author$ constructor(db: AngularFireDatabase) { this.courses$ = db.list('/courses ...

What is the best way to create a Typescript type consisting of only the public members of a different type?

Inside the realm of Typescript 4.3.5 In what manner can I establish a type that consists solely of the public members and properties of another type? Take into account: class Thing { public name: string private secret: string public greet(): string ...

Exploring the depths of Javascript objects using Typescript

If I have this specific dataset: data = { result: [ { id: '001', name: 'Caio B', address: { address: 'sau paulo', city: 'sao paulo', ...

transform json array into a consolidated array by merging identical IDs

I need to transform an array into a different format based on the values of the ID and class properties. Here is the initial array: const json = [{ "ID": 10, "Sum": 860, "class": "K", }, { "ID": 10, "Sum": 760, "class": "one", }, { "ID": ...

Resolve the type of the combineLatest outcome in RxJS

Encountering this scenario frequently in Angular when working with combineLatest to merge 2 observables that emit optional values. The basic structure is as follows: const ob1: Observable<Transaction[] | null>; const ob2: Observable<Price[] | nul ...

Increasing the number of service providers in Angular2-4 directives

Is there a way to apply both * to a string? Below is the code snippet I am working with: <a class="sidenav-anchor" *ngIf="!item.hasSubItems()" md-list-item md-ripple [routerLink]="[item.route]" routerLinkActive="active" [routerLinkActiveOptions]="{ex ...

Display a free Admob banner within an Ionic 3 application

I have integrated Admob's banner into my Ionic 3 app following the guidelines provided in the Ionic documentation at this link. Below is the code snippet I used for displaying the banner on the homepage: import { Component } from '@angular/core ...

Updating the status of various sections with Redux toolkit

Is it possible to update the state of a store, which consists of multiple slices, with a new state in React using Redux Toolkit? While you can revert the entire store to its initial state using extraReducers, is it also possible to change the state to som ...

A unique Angular decorator is created to seamlessly integrate a new component within an existing component

I'm currently experimenting with creating a decorator in Angular that will display a spinner on top of the main component. Essentially, my main component is making API requests and I want to overlay the decorator on top of the method. This is how I ...

Developing a custom React component library using Webpack and Typescript, however encountering issues with Webpack consistently bundling React

I'm currently in the process of developing an external component library using Webpack and TypeScript. Although it compiles without any issues, I encounter an error when attempting to use the library: Invalid hook call. Hooks can only be called ins ...

Utilizing @ngrx/router-store in a feature module: A comprehensive guide

The NGRX documentation for Router-Store only showcases an example with .forRoot(). Upon experimenting with .forFeature(), I realized that this static method does not exist. I am interested in defining certain actions and effects to be utilized within my f ...

Utilizing Arrays in Typescript within the Angular Framework

I have developed a Rest API that provides data to populate two drop-down lists in a form. The information retrieved from the API is grabbed by the Angular backend and assigned to the respective drop-downs. Rather than making separate Get requests for each ...

Is it possible to verify a file's type with Nestjs Pipes and the FileTypeValidator?

I am facing an issue with implementing a Nestjs route in a controller that includes a file upload using Multer. The goal is to edit a user's profile picture, so I need to validate that the uploaded file is an image. However, despite using the FileType ...

"Typescript with React and Material-UI Table - A seamless user experience with no errors

I have been working on incorporating the "material-table" library into my TypeScript and React project, but I am facing an issue where the page appears blank without any compiling errors. Environment configuration: npm: 6.11.3 nodejs: 10.17.0 typescript: ...

Error: Trying to access 'MaterialModule' before it has been initialized results in an uncaught ReferenceError

I have been working on a form for a personal project and attempted to implement a phone number input using this example: . However, after trying to integrate it into my project, I encountered an error. Even after removing the phone number input code, the e ...