Resolving the issue of missing properties from type in a generic object - A step-by-step guide

Imagine a scenario where there is a library that exposes a `run` function as shown below:

runner.ts

export type Parameters = { [key: string]: string };
type runner = (args: Parameters) => void;

export default function run(fn: runner, params: Parameters) {
    fn(params);
}

Now, consider the code snippet in another file: index.ts

import type { Parameters } from "./runner.ts";
import run from "./runner.ts";

type CustomParams = { hello: string };

function logGenericArgs(args: Parameters): void {
    console.log(args);
}

function logHelloFromArgs(args: CustomParams): void {
    console.log(args.hello);
}

run(logGenericArgs, { abc: "123" });
run(logHelloFromArgs, { hello: "123" });  /* TypeScript errors occur due to types being incompatible.
  It appears that the 'hello' key in 'CustomParams' does not match the signature of 'Parameters'.
*/

Why does TypeScript raise an issue about the mismatched types, even though they seem compatible? The `Parameters` type signifies a generic object with string keys and values, while `CustomParams` includes a "hello" key that aligns with the structure defined by `Parameters`.

Is there a way to modify the "runner" library's code so that it can accept varying object types and seamlessly interact with them? I prefer not to resort to using 'unknown' or 'any', as they offer limited utility. My goal is for the `run` function's declaration to signify that `args` is an object without restricting it solely to that specific structure. Additionally, I want to refrain from marking the 'hello' key in 'CustomParams' as optional, as it should be mandatory when utilizing the `CustomParams` type, and I do not wish to include 'hello' in the `Parameters` type as it may not be necessary in every usage scenario associated with the "runner" library.

Answer №1

In TypeScript, it's important to properly associate parameters with their corresponding functions. One way to do this is by adding a generic parameter:

type Parameters = { [key: string]: string };
type runner<P extends Parameters = Parameters> = (args: P) => void;

function execute<P extends Parameters>(fn: runner<P>, params: P) {
    fn(params);
}

When you use your custom parameters function:

execute(logMessageFromArgs, { message: "Hello World" });

The inferred type is:

function execute<{
    message: string;
}>(fn: runner<{
    message: string;
}>, params: {
    message: string;
}): void

This alignment ensures that the function behaves as intended.

Interactive Example

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

Distribute a TypeScript Project on NPM without exposing the source code

Issue: My library consists of numerous .ts files organized in structured folders. As I prepare to publish this library, I wish to withhold the source (typescript) files. Process: Executing the tsc command results in the creation of a corresponding .js fil ...

Create Angular file structures effortlessly using a tool similar to Rails scaffold

Is there a code generator in Angular similar to RoR's rails scaffold? I am looking to run a specific command and receive the following files, such as: *.component.html *.component.sass *.component.ts *.module.ts. ...

Is it possible to declare language features in Typescript? For example, changing `!variable` to `no variable`

Can Typescript language features be declared within the app's source code? I want to enhance code clarity by implementing a small feature. Modified Null Test if (no userDetails) { // handle null } This new null test syntax is a little more conc ...

Encountering Duplicate Identifier Error while working on Angular 2 Typescript in Visual Studio Code

Currently attempting to configure a component in Angular 2 with Typescript using Visual Studio Code on Mac. Encounter the following errors when trying the code below: duplicate identifier 'Component'. and Duplicate identifier' DashboardCompo ...

What steps can I take to prevent encountering a Typescript Error (TS2345) within the StatePropertyAccessor of the Microsoft Bot Framework while setting a property?

During the process of constructing a bot in Typescript, I encountered TS2345 error with Typescript version 3.7.2. This error is causing issues when attempting to create properties dynamically, even if they are undefined, or referencing them in the statePro ...

Is there a way to retrieve the Incoming Message object in Angular using HttpClient?

From my Angular file, I am sending this request to the server. Is there a way to access it from my typescript file using a HttpClient object? IncomingMessage { ... // Headers details omitted for brevity url: '/teradata/databases/view/djfg', ...

Properties of a child class are unable to be set from the constructor of the parent class

In my current Next.js project, I am utilizing the following code snippet and experiencing an issue where only n1 is logged: class A { // A: Model constructor(source){ Object.keys(source) .forEach(key => { if(!this[key]){ ...

Passing an array of items as a property to a child component in React with Typescript is not possible

In my project, I have multiple classes designed with create-react-app. I am trying to send an array of objects to child components as illustrated below. Items.tsx import * as React from 'react'; import ItemTable from './ItemTable'; imp ...

Leveraging non-React entities to seamlessly integrate components within a component hierarchy in React utilizing TypeScript

I am currently working on a React Typescript project where I am exploring the use of traditional polymorphism. Below is a simplified version of my project, where components are returned from vanilla Typescript objects rather than React components, allowing ...

Use the ngFor directive to iterate over the most recently created array from the parent ng

I am looking to link material tabs with ngFor to generate arrays for child ngFor. Let's start from the beginning: <mat-tab-group> <mat-tab *ngFor="let tab of asyncTabs "> <ng-template mat-tab-label>{{tab.label}}</ng-template ...

Troubleshooting a Pulumi script in Azure using Typescript and encountering difficulties with function writing

My background is in using terraform, but now I am trying out Pulumi/typescript for the first time. In my codebase, I have two files - index.ts and blob.ts. The create function in blob.ts is responsible for creating a storage account, resource group, blob ...

In TypeScript, inferring argument types

Here is the code snippet in question: type Inferred<T> = T extends (...args: (infer UnionType)[]) => any ? UnionType : never function f(first: 'first', second: 'second', bool: boolean) {} type T = Inferred<typeof f> // ...

Issue encountered when attempting to import a module within the ionic framework

I encountered an issue in my project (built with the ionic-framework 3) where I included the following line to import the dialogflow module: const dialogflow = require('dialogflow'); However, when compiling, it resulted in the error message: ...

Creating a conditional statement within an array.map loop in Next.js

User Interface after Processing After retrieving this dataset const array = [1,2,3,4,5,6,7,8] I need to determine if the index of the array is a multiple of 5. If the loop is on index 0, 5, 10 and so on, it should display this HTML <div class="s ...

parsing objects within an HTML component in Angular

Is there a way to utilize an object as the @input parameter in HTML? For example: <app-home [param]="user.salary"></app-home> However, the type of my user object is structured like this: user:Person=new Employee(); The classes invol ...

Issues with accessing view variables within a directive query are persisting

I am struggling with a specific directive: @Directive({ selector: '[myDirective]' }) export class MyDirective implements AfterViewInit { @ViewChild('wrapper') wrapper; @ViewChild('list') list; ngAfterViewInit() { ...

What function is missing from the equation?

I am encountering an issue with an object of type "user" that is supposed to have a function called "getPermission()". While running my Angular 7 application, I am getting the error message "TypeError: this.user.getPermission is not a function". Here is w ...

Utilizing winston to generate multiple log files with set maximum sizes and daily rotation

Currently, I am utilizing winston for logging with a maximum size and daily rotation. I am interested in having this functionality with one file per API endpoint to define multiple log files. Is there a way to achieve this? Displayed below is my winston ...

Jaydata is a powerful open source library for interacting with databases

I rely on jaysvcutil for compiling OData $metadata and generating JayDataContext.js, which is truly impressive. However, I prefer to work with Typescript without using import/export syntax or other third-party tools like requirejs or systemjs. Even thoug ...

Utilize rest parameters for destructuring操作

I am attempting to destructure a React context using rest parameters within a custom hook. Let's say I have an array of enums and I only want to return the ones passed into the hook. Here is my interface for the context type enum ConfigItem { Some ...