Tips to declare a return type for a function that includes an optional formatter function parameter

I'm currently exploring how to correctly define the return value when working with an object that includes optional formatter functions.

Everything is running smoothly for a function with a single value.

type Params = {
    id?: number
    created?: string
}

type FormatFn<TParam extends keyof Params, TValue> = (data: Params[TParam]) => TValue

type Result<TValue> = {
    param: TValue
}

declare function query<TParam extends keyof Params, TValue = Params[TParam]>(
    param: TParam,
    formatter?: FormatFn<TParam, TValue>
): Result<TValue>;

// The functionality works as intended, where 'created' is typed as 'Date'
const { param: created } = query('created', (created) => new Date(created || ''))

I am interested in creating a version where you can input both an array of strings and an object containing optional formatter functions.

Here is a Playground showcasing my attempt.

This is what I expect as the output:

queries(['id', 'created'], {
    created: (created) => new Date(created || '')
})

// Instead of the original return type
// {
//     params: {
//         id: number,
//         created: string
//     }
// 
// I am aiming for this structure due to the formatter function
// {
//     params: {
//         id: number,
//         created: Date
//     }
// }

Answer №1

type Params = {
    id?: number
    created?: string
}

type Elem = keyof Params;

type Fn = (value: any) => any

type Predicate<T extends Elem> = Record<T, (value: Required<Params>[T]) => any>

type Reducer<
    Arr extends ReadonlyArray<Elem>,
    Result extends Record<string, any> = {}
    > = Arr extends []
    ? Result
    : Arr extends readonly [infer H, ...infer Tail]
    ? Tail extends ReadonlyArray<Elem>
    ? H extends Elem
    ? Reducer<Tail, Result & Predicate<H>>
    : never
    : never
    : never;

/**
 * JavaScript/TypeScript comparison
 */

const reducerPredicate = (elem: string) => ({ [elem]: () => null });

const reducer = <Arr extends string[]>(
    arr: Arr,
    result: { [prop: string]: string } = {}
) => {
    if (arr.length === 0) {
        return result; // 1. this marks the end of recursion
    }

    const [head, ...tail] = arr; // 2. from the first element till the second last one

    return reducer(tail, { ...result, ...reducerPredicate(head) });
    // no need for 'never' branch here
};

type Result<
    P extends ReadonlyArray<keyof Params>,
    Defaults extends Partial<Record<keyof Params, Fn>>,
    Cache extends P[number] & keyof Defaults = P[number] & keyof Defaults> =
    & { [Prop in Exclude<P[number], Cache>]: Required<Params>[Prop] }
    & { [Prop in keyof Defaults]: Defaults[Prop] extends Fn ? ReturnType<Defaults[Prop]> : never }

function queries<TParams extends Array<keyof Params>, TValues extends Partial<Reducer<TParams>>>(
    params: [...TParams],
    formatter?: TValues
): Result<TParams, TValues> {
    return null as any
}

const multiple = queries(['id', 'created'], {
    created: (created /** string */) => 42,
    id: (value /** number */) => 42
})

Try it yourself on TypeScript Playground

For more insights, visit my blog here.

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

Exploring ways to fetch an HTTP response using a TypeScript POST request

I have been looking at various questions, but unfortunately, none of them have provided the help I need. The typescript method I am currently working with is as follows: transferAmount(transfer: Transfer): Observable<number> { return this.http .po ...

I am unable to employ filtering in TypeScript

Hey there, I'm trying to filter some JSON data randomly by using this function, but I keep running into an error with my variable called filteredArray. The error message says "Property 'filter' does not exist on type 'Dispatch<SetSta ...

Prevent accidental misuse of object[index] in Typescript

It caught me off guard when I discovered that TypeScript allows the following code: interface IFoo { bar: string; } const foo: IFoo = {bar: 'bar'}; console.log( foo['anything'] // I want TypeScript to stop this ); Is there a way ...

Absolute file path reference in Node.js

I'm working on a Node.js project using WebStorm IDE. Here's the structure of my project: The root folder is named "root" and inside are 2 folders: "main" and "typings". The "main" folder has a file called "foo.ts", while the "typings" folder co ...

Webpack is encountering difficulties in locating the entry module when working with typescript

I've been working on integrating webpack into my typescript application. To get a better understanding of webpack, I decided to do a minimal migration. I started by cloning the Angular2 quickstart seed and added a webpack.config.js: 'use strict& ...

What is the process for importing string data into an Excel document using Angular?

I'm encountering a situation where I have non-JSON data coming from the backend. How can I efficiently write this type of data into an Excel file? To handle this task, I've utilized XLSX and FileSaver libraries by referencing an example on Plunk ...

Why is it necessary for the required type of a function parameter to be able to be assigned to

From Optional to Required Type const testFunc = (func: (param: number) => void): void => { func(3); }; testFunc((a?: number) => { console.log(a); }); From Required to Optional Type const testFunc = (func?: (param: number) => void): void = ...

Warning in TypeScript when attempting to modify a single state property within a React component

Imagine we have a simple react Component: import React, { Component } from 'react'; interface IState { a: boolean; b: string; c: number; } class Test extends Component<{}, IState> { state = { a: true, b: 'value' ...

Whenever I attempt to execute yarn build within next.js, an error always seems to occur

When attempting to compile my next.js project using the yarn build command, an error consistently occurs: Error: Export encountered errors on following paths: /settings at D:\web3\futnft\frontend\node_modules\next\ ...

Guide to testing error throwing in error events with Jest

I am having an issue with a particular learning case. The code snippet below is what I am dealing with and I aim to test error throwing: export const someFunction = async () => { //... const fileReadStream = createReadStream(absoluteFilePath) .on(&a ...

Filtering an array of objects based on a specific condition in TypeScript

I am trying to filter the array object data where the count of Failed is greater than 0. Unfortunately, the code below is not working as expected. ngOnInit() { this.employeeService.getProducts().subscribe((data:any) => { console.log(data); this. ...

What is the method for utilizing string interpolation in Angular/Typescript in order to retrieve a value from a variable?

I have a variable called demoVars, which is an array of objects with properties var1, var2, and var3. In my component class, I have a variable named selectedVar that holds the name of one of these properties: var1, var2, or var3. I want to dynamically pu ...

Encountering difficulty in retrieving the outcome of the initial HTTP request while utilizing the switchMap function in RxJS

My goal is to make 2 HTTP requests where the first call creates a record and then based on its result, I want to decide whether or not to execute the second call that updates another data. However, despite being able to handle errors in the catchError bl ...

Obtaining the date without the time in TypeScript and MongoDB

I'm working with TypeScript and have the following code snippet: const EmployeeDetailsSchema: mongoose.Schema = new mongoose.Schema({ employeeId: { type: String }, advance: { lastAdvanceClosedOn: { type: String }, pending: { type: String ...

The expected input should be either an HTMLElement or an SVGElement, but the received input is currently null

Below is the code for a component: function SignUpPage() { return ( <> <h1>Sign Up</h1> <input name="userName" /> </> ); } export default SignUpPage; Testing the component: it("should c ...

What is the alternative to the deprecated 'combineLatest' method in rxJs and how can it be replaced?

Recently, I came across a situation where I had implemented a method using the combinlatest rsjx/operator. It was working perfectly fine. However, Sonar flagged it as deprecated and now I need to update it to the latest version. When I tried to simply re ...

The TN-Models-FP error message states that it is not allowed to use the `create` model without an associated `entity` model

Utilizing the TN-models-fp library to construct a basic api inspired by the provided examples, here is my implementation in api.ts: import { axios } from '../axios-instance' import { createApi } from '@thinknimble/tn-models-fp' import { ...

What is the process of launching an Angular component in a new browser tab?

I have a large amount of data that needs to be displayed on the screen in a simplified list format for users to choose an item and view its details. Consider a component called SimpleListComponent, which will store the data and present a condensed view: ...

Transform the date format from Google Forms to TypeScript

I am currently facing an issue with a Google Form connected to a Google Spreadsheet. The date format in the spreadsheet appears as follows when a response is received: 20/02/2023 18:58:59 I am seeking guidance on how to convert this date format using Type ...

The functionality of provideRouter and RouterConfig cannot be located in the updated @angular/router version 3.0.0-alpha.3^

Currently in the process of migrating an angular2 app to RC2 and experimenting with the router's version 3 alpha. Followed the setup provided by the angular docs for routing as demonstrated in the plunker. However, encountering the following errors: ...