What is the best way for a function to accommodate various types that adhere to an interface?

How can we create a function that can accept multiple types with a common interface?

Let's consider the example:

interface A {a: number}

type B = {b: string;} & A

type C = {c: string;} & A


function acceptA(a: A) {
  return a
}

acceptA({a: 2, c: "type-error"})

Type error:

Argument of type '{ a: number; c: string; }' is not assignable to parameter of type 'A'. Object literal may only specify known properties, and 'c' does not exist in type 'A'.

The acceptA function currently doesn't allow an argument of type C. Is there a way to modify acceptA to accommodate any type that meets the requirements of interface A?

Answer №1

When TypeScript's structural type system considers {a: number, c: string} to be assignable to {a: number}, it may seem confusing that the compiler complains when passing

{a: 2, c: "type-error"}
to a function expecting a value of type A.

The reason for this is known as excess property checking. When an object literal with more properties than expected is used in a context where a simpler type is anticipated, the compiler warns because it assumes you might be discarding important information:

If the acceptA function only expects objects of type

A</code, additional properties like <code>c
become inaccessible within acceptA. Thus, passing such object literals leads to potential loss of data. The extra property becomes redundant both inside and outside the scope of
acceptA</code.</p>
<p>To avoid this warning, one approach is to save the object literal into a variable first, permitting access to all its properties:</p>
<pre><code>const obj = { a: 2, c: "okay" }
// const obj: {a: number, c: string}
acceptA(obj); // No issue now

Before attempting workarounds, ensure excess property checking does not serve a purpose in your specific scenario. If desired, modifications can be made to allow for object literals with surplus properties without triggering warnings.


One solution involves explicitly defining that acceptA accepts values conforming to type A alongside a more general "anything-goes" object type like {[k: string]: any}:

function acceptA(a: A & { [k: string]: any }) {
  a.c // any
  return a
}

acceptA({ a: 2, c: "okay" })

By doing so, all property keys are expected, eliminating the concept of excess properties. Although not completely foolproof, this method reduces the likelihood of losing type-specific information.


Alternatively, making use of generics in the type definition for the argument passed to acceptA can also address the excess property check concerns:

function acceptA<T extends A>(a: T) {
  return a
}

acceptA({ a: 2, c: "okay" })

This implementation allows the compiler to maintain awareness of the specific type being utilized, reducing the risk of sacrificing valuable type details. While neither approach guarantees preservation of type information, they offer ways to navigate around excess property checks.


Note that excess property checking is just one of many precautions enforced by the TypeScript compiler, aimed at promoting code consistency and minimizing errors. Understanding these checks and their implications is essential for writing robust and reliable TypeScript applications.

Click here to view 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

Developing a specialized command-line application for currency conversion is my current project

Currently, I am working on developing a command-line application for currency exchange. I have created an interface to define the structure of an object array that will store the keys and values of currency names along with their current values in the inte ...

What is the best way to download a file with a specific name using Angular and TypeScript?

Greetings! Below is a snippet of code from my Angular component: this.messageHistoryService.getMessageHistoriesCSV1(msgHistoryRequest).subscribe( (data) => { console.log(data.messageHistoryBytes); let file = new Blob( [data.messageHistoryBytes ...

TypeScript and Next.js failing to properly verify function parameters/arguments

I'm currently tackling a project involving typescript & next.js, and I've run into an issue where function argument types aren't being checked as expected. Below is a snippet of code that illustrates the problem. Despite my expectation ...

How can different styles be seamlessly combined when customizing Fabric components?

Imagine I am enhancing a Fabric component by adding custom styles and wishing to combine any additional styles passed in through its props. Here's the solution I've devised: const { TextField, Fabric , IButtonProps, mergeStyleSets } = window.Fab ...

Issue with subscribing to nested observables, unable to successfully unsubscribe

My app is using Firebase auth with Firestore (https://github.com/angular/angularfire2). Despite my efforts to unsubscribe from all observables fetched from Firestore before signing out, I keep encountering a "FirebaseError: Missing or insufficient permissi ...

Retrieve the raw text from the input field instead of the date object

Below is the HTML code for an input field: <div class="input-group input-group-md"> <input id="date" name="date" [readOnly]="disabled" type="text" placeholder="M0/d0/0000 Hh:m0:s0" [placeholder]="pl ...

What is the best way to access all the attributes (excluding methods) of an object in a class instance?

My goal is to generate a new object type that inherits all the properties from an existing class instance. In other words, I am looking for a way to transform a class instance into a plain object. For example, consider the following scenario: ...

Steps for incorporating a toggle feature for displaying all or hiding all products on the list

Looking for some guidance: I have a task where I need to display a limited number of products from an array on the page initially. The remaining items should only be visible when the user clicks the "Show All" button. Upon clicking, all items should be rev ...

Issue regarding retrieving the image using TypeScript from an external API

Hey developers! I'm facing an issue with importing images from an external API. I used the tag: <img src = {photos [0] .src} /> but it doesn't seem to recognize the .src property. Can anyone shed some light on how this is supposed to work? ...

A long error occurred while using the payloadaction feature of the Redux Toolkit

import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit" import axios, { AxiosError} from "axios" type user = { id: number, token: string } export type error = { error: string } interface authState { user: user | ...

Achieving asynchronous results in the parent function with TypeScript: a guide

The code structure provided is as follows: import {socket} from './socket'; class A{ Execute(...args[]){ //logic with Promises SomeAsyncMethod1().then(fulfilled1); function fulfilled1(){ SomeAsyncMethod2(args).then(fulfilled2); ...

Webpack is having trouble identifying Node's process module

It has been more than ten years since I last worked with JavaScript, but recently I had an idea for an app that would be best implemented as a NodeJS app. As I delved into the modern JS ecosystem, like many others, I found myself thoroughly confused, haha. ...

Storing Json data in a variable within Angular 2: a step-by-step guide

Within the params.value object, there are 3 arrays containing names that I need to extract and store in a variable. I attempted to use a ForEach loop for this purpose, but encountered an issue. Can you spot what's wrong? var roles = params.forEach(x ...

There seems to be an issue with the response type in the node.js environment

I am currently working on node.js and typescript, but I am encountering a minor issue. Below is the routeController I have created: public allUsers = (req: Request, res: Response) => { res.status(500).json({ status: "ERROR", ...

Monitor and compile numerous directories simultaneously in TypeScript (monorepo)

I've been searching far and wide online for a solution to my problem, but unfortunately, I haven't come across anything useful yet. Essentially, I am in need of a tool or method that will allow me to kick off TypeScript file Watching/Compiling in ...

Have you noticed the issue with Angular's logical OR when using it in the option attribute? It seems that when [(ngModel)] is applied within a select element, the [selected] attribute is unable to change

Within the select element, I have an option set up like this: <option [selected]=" impulse === 10 || isTraining " value="10">10</option> My assumption was that when any value is assigned to impulse and isTraining is set to true, the current o ...

Angular 6 Error: Unable to access property 'e4b7...f' as it is undefined

I'm encountering an issue while trying to initialize an object based on a TypeScript interface. Even though I am assigning a value, I still receive an error stating that the property is undefined. interface ITableData { domainObjectName: string; ...

Verify the validity of an image URL

I am attempting to create a function in TypeScript that checks the validity of an image source URL. If the URL is valid, I want to display the image using React Native Image. If the URL is invalid, I would like to replace it with a local placeholder imag ...

Angular two - Communication between parent and children components using a shared service

I am currently working on establishing communication between child components, but according to the documentation, I need to utilize services for this purpose. However, I am facing challenges in accessing service information. When I try to assign the retur ...

Error in Nestjs Swagger: UnhandledPromiseRejectionWarning - The property `prototype` cannot be destructed from an 'undefined' or 'null' object

Currently, I am in the process of developing a Nestjs REST API project and need to integrate swagger. For reference, I followed this repository: https://github.com/nestjs/nest/tree/master/sample/11-swagger However, during the setup, I encountered the foll ...