How to prevent redundant object declarations when passing parameters in TypeScript?

Motivation for Using Object Parameters

One of the motivations behind using objects as function parameters is to allow the caller to clearly define arguments with specified field names, which can make code reviews easier.

Challenge When Using Implements and Extends

However, dealing with object parameters in conjunction with implements and extends can become cumbersome. This is currently a challenge that I am facing in my code implementation.

src/domain/ServiceInterface.ts

export interface ServiceInterface {
  doesThings(args: {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  }): boolean;

src/domain/ComposedServiceInterface.ts

import { ServiceInterface } from "./domain/ServiceInterface";
export type ComposedServiceInterface = ServiceInterface & { hello: () => string };

src/implementations/ComposedServiceImplementation.ts

import { ComposedServiceInterface } from "./domain/ComposedServiceInterface";
export class ComposedServiceImplementation implements ComposedServiceInterface {
  doesThings(args: {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  }): boolean {
    return true;
  }
}

Various Approaches Taken

1. Using type / interface for Object Parameter

src/domain/ServiceInterface.ts

export type DoesThingsParameter = {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  };
export interface ServiceInterface {
  doesThings(args: DoesThingsParameter): boolean;

src/domain/ComposedServiceInterface.ts

import { ServiceInterface } from "./domain/ServiceInterface";
export type ComposedServiceInterface = ServiceInterface & { hello: () => string };

src/implementations/ComposedServiceImplementation

import { ComposedServiceInterface } from "./domain/ComposedServiceInterface";
import { DoesThingsParameter } from "./domain/ServiceInterface";
export class ComposedServiceImplementation implements ComposedServiceInterface {
  doesThings(args: DoesThingsParameter): boolean {
    return true;
  }
}

Concern: The use of import in

src/implementations/ComposedServiceImplementation
might not be necessary since it only implements ComposedServiceInterface

2. Exploring Utility Types such as Parameter

Reference: https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype

I have encountered challenges in getting TypeScript to accept a Class method like

Parameter<ComposedServiceInterface.doesThings>
. Any advice or insights on this would be appreciated.

Answer №1

One effective method for reusing types/interfaces is to separate them into a common file, as demonstrated in Attempt#1. By doing this, you can easily import these definitions wherever needed without cluttering your codebase. If you're concerned about importing from src/domain/ServiceInterface in

src/implementations/ComposedServiceImplementation
, creating a central file for shared types and importing it in both locations is a good practice.

It's worth noting that when you only need type information from src/domain/ServiceInterface, this import will be stripped out during the TypeScript compilation process. To distinguish between importing types versus values, consider using a "type-only" import syntax like:

import type { DoesThingsParameter } from "./domain/ServiceInterface";
This can improve the readability of your code.

Regarding Attempt#2, it's recommended to avoid excessively using complex utility types such as Parameter or ConstructorParameter. These types involve intricate implementations that may burden TypeScript with extra work. Typically, they are utilized to infer types for arguments not directly accessible.

// some-library.ts
export function configure({
 config1 = 'somevalue',
 retries = 1
}) {
  ...code
}

For instance, if you need to utilize a function from a library lacking TypeScript definitions, and you wish to define a configuration object externally:

// helper.ts
import { configure } from 'some-lib';

const configuration = {
 config1: 'overrideValue',
 unknownProperty: 'unknownValue'
}
function helper() {
 const lib = configure(configuration)
...perform tasks here
 }

In this scenario, the configuration object includes all expected properties for the configure function but adds an additional one not present in its arguments. Here, employing ParameterType becomes necessary to acquire the type definition for the configure function's arguments.

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

Error: Typescript error at line 18 in app.ts - Cannot locate the 'server' namespace

Check out this straightforward code snippet: "use strict"; import * as express from "express"; class Server { public app: express.Application; public static start(): Server { return new Server(); } constructor() { this. ...

How to selectively make properties optional in Typescript conditions

Currently, I am working on creating a utility type to unwrap nested monads of Options in my code. Here is the progress I have made so far: export interface Option<T> { type: symbol; isSome(): boolean; isNone(): boolean; match<U>(fn: Mat ...

Encountering issues with MediaSession.setPositionState() and seekto functionalities not functioning properly

Having trouble with MediaSession.setPositionState() not displaying the audio time and seekbar not behaving as expected. const sound= document.querySelector('sound'); function updatePositionState() { if ('setPositionState' in navigato ...

Typescript declaration for a .js file in a diverse project

Hey there! I'm currently in the process of converting my JavaScript React Redux project to TypeScript, and I've decided to kick things off by tackling my redux reducers file. Here's a snapshot of how my project is structured: Project/ .. ...

Guide on how to showcase the template by leveraging the roomList information with ngTemplateOutlet in Angular

TS roomList = [{ name: 'Room2' }] HTML <div class="Layout-body"> <ng-container *ngFor="let dt of roomList; index as i" [ngTemplateOutlet]="Room1" [ngTemplateOutletContext]="{ data: dt, i: i }&qu ...

Type returned by a React component

I am currently using a basic context provider export function CustomStepsProvider ({ children, ...props }: React.PropsWithChildren<CustomStepsProps>) => { return <Steps.Provider value={props}> {typeof children === 'function&ap ...

The concept of a singleton design pattern is like a hidden treasure waiting to be

My approach to implementing the singleton pattern in a typescript ( version 2.1.6 ) class is as follows: export class NotificationsViewModel { private _myService: NotificationService; private _myArray: []; private static _instance: Notificatio ...

Dynamic rows in an Angular 2 Material data table

I'm currently working on dynamically adding rows to an Angular 2 Data Table ( https://material.angular.io/components/table/overview) by utilizing a service called "ListService". This service provides me with the columns ("meta.attributes") to be displ ...

Having difficulty resolving sub-modules using webpack

Currently, I am trying to set up the @microsoft/signalr npm package with webpack by importing the module using import * as signalR from '@microsoft/signalr'. However, I encountered an error message indicating that webpack is unable to resolve the ...

What could be causing the availability of a response in a service, but showing as undefined in the component?

Currently, I am facing a problem with my service and component setup. While the service can successfully read the response as a JSON object, the component is returning res: undefined. service: constructor( private http: Http, private fbuilder: Fo ...

Unit Testing with Angular: Testing the setValueControl function

I am currently in the process of writing unit tests for a straightforward function that assigns controls to various values. fillFormAssociazioneVeicolo() { if (this.aaa) { setValueControl( this.aaa.targaTelaio, this.form.get(&apos ...

In an Angular component, attempt to retrieve the data type of a class property

Discover how to retrieve the type of properties from an object by following this Typescript tutorial link. However, it seems to be behaving like Javascript and returning the value of the property instead of the type: const x = { foo: 10, bar: 'hello! ...

Encountering the "Unrecognized teardown 1" error when subscribing to an Observable in Typescript and Angular2

Having trouble with using an Observable in my Angular2.rc.4 Typescript app. Check out the plunker for it here: https://embed.plnkr.co/UjcdCmN6hSkdKt27ezyI/ The issue revolves around a service that contains this code: private messageSender : Observable< ...

Certain sections within a Formik form are failing to update as intended

I have successfully implemented a custom TextField wrapper for Material-UI fields, but I am facing an issue with native Material UI fields not updating the form data upon submission. Below is the relevant code snippet along with a link to a code sandbox d ...

Jest is having trouble locating the module that ends with ".svg?react" when using Vite in combination with React, TypeScript, and Jest

Currently, I am facing an issue while testing my app built using vite + react + ts. Jest is highlighting an error stating that it cannot locate the "svg?react" module when trying to create my dashboard component. The problem arises with Jest as soon as th ...

Angular validation with input binding using if statement

I have developed a reusable component for input fields where I included a Boolean variable called "IsValid" in my typescript file to handle validation messages. Here is the code from my typescript file: export class InputControlsComponent implements OnIn ...

What is the process for creating a coverage report for a TypeScript extension in vscode?

It appears that generating coverage reports with coveralls is not feasible for a VSCode extension built with TypeScript. Currently, I am incorporating test cases into our project https://github.com/PicGo/vs-picgo/pull/42. Despite exploring various methods ...

Retrieving a data type from the key values of deeply nested objects

I'm currently working with JSON data that contains nested objects, and my goal is to extract the unique set of second-level keys as a type. For instance: const json = { 'alice': { 'dogs': 1, 'birds': 4 ...

The function signature '({ articles }: Props) => JSX.Element' does not match the type 'NextPage<{}, {}>'

Recently, I've decided to delve into the world of React.js and Next.js after being familiar with Vue.js. Encountering a peculiar typescript error has left me scratching my head, but surprisingly, the code actually compiles despite Visual Studio Code w ...

retrieve asynchronous data from the server using ngrx

How can I retrieve asynchronous data from the server? I am looking to save this data in a global store for future updates. I'm having trouble grasping the concept of asynchronous calls, such as in Redux. While I was able to understand it with simpl ...