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

What are some strategies for circumventing the need for two switches?

My LayerEditor class consists of two private methods: export class LayerEditor { public layerManager: LayerManager; constructor() { this.layerManager = new LayerManager(this); } private executeCommand() { ...

Tips for defining the types of nested arrays in useState

Looking for guidance on how to define types for nested arrays in a useState array This is my defined interface: interface ToyProps { car: string | null; doll: number | null; } interface SettingsProps { [key: string]: ToyProps[]; } Here is the stat ...

Beautiful ExpressionChangedAfterItHasBeenCheckedError

I need your help Input field where I can enter a Student email or IAM, which will be added to a string array List displaying all the students I have added, using a for loop as shown below Delete button to remove a student from the array The list has a sp ...

Leveraging ArangoJS Driver within an Angular2 web platform

Currently, I am in the process of working on a project that involves Angular2 and Typescript (v1.8.10). Our aim is to incorporate data from an ArangoDB database into the web application. Ideally, I would like to utilize the arangojs driver for this task. H ...

What could be causing Typescript Compile Errors to occur during runtime?

In the Visual Studio React + Redux template project, I have created a react component with the following "render()" method: public render() { return ( <React.Fragment> <h1>Welcome to the Adventure Company {th ...

Is it possible for an object to be undefined in NextJS Typescript even after using a guard

Hey there, I could really use some help with a React component I'm working on. This is one of my initial projects and I've encountered an issue involving fetching async data. Below is the current state of my solution: import { Component } from &q ...

How can I combine multiple requests in RxJS, executing one request at a time in parallel, and receiving a single combined result?

For instance, assume I have 2 API services that return data in the form of Observables. function add(row) { let r = Math.ceil(Math.random() * 2000); let k = row + 1; return timer(r).pipe(mapTo(k)); } function multiple(row) { let r = Math.c ...

What is the best way to define a named, optional parameter in TypeScript-React that can accept an object with optional fields and default values?

Is there a way to achieve something similar to the style property found in default HTML components? For instance: <div ref={ref} style={{ overflow: 'visible', height: 600, width: '100%' }}> The style prop is optional, and the fie ...

I'm having trouble grasping the concept of 'globals' in TypeScript/NodeJS and distinguishing their differences. Can someone explain it to me?

As I review this code snippet: import { MongoMemoryServer } from "mongodb-memory-server"; import mongoose from "mongoose"; import request from "supertest"; import { app } from "../app"; declare global { function s ...

Jasmine attempting to access a nonexistent property

I created a very basic component: import { Component } from '@angular/core'; @Component({ selector: 'loading', templateUrl: './loading.component.html', styleUrls: ['./loading.component.scss'] }) export ...

React: Issue with passing arguments to redux action hooks

In my React application, I have implemented Redux-Toolkit to manage reducers and actions with slices. I am currently working on creating actions that can update and delete values from the store, requiring arguments for their usage. To achieve this, I have ...

The password encryption method with "bcrypt" gives an undefined result

import bcrypt from 'bcrypt'; export default class Hash { static hashPassword (password: any): string { let hashedPassword: string; bcrypt.hash(password, 10, function(err, hash) { if (err) console.log(err); else { ha ...

Searching Local JSON Data in Ionic 4 with a Filter Input Bar

My current challenge involves filtering local JSON data in my Ionic project. Despite referencing other resources, I am unable to filter or display filtered items on the ngx-datatable. I suspect the issue may lie either in the filterItems function implement ...

Searching for evenly distributed GPS coordinates across an area

I have a collection of data points representing mountain peaks in the European Alps that I would like to showcase on a map. To prevent cluttering the user interface, I currently retrieve the highest peaks within the visible area of the map by sorting them ...

Monitoring a Typescript Class's Get() or Set() function using Jasmine Spy

While working with Jasmine 2.9, I have encountered no issues spying on both public and private functions, except for when trying to spy on a get or set function at the class level. private class RandomService { public dogsHealth = 0; private get pers ...

In order to iterate through a 'IterableIterator<number>', you must either enable the '--downlevelIteration' flag or set the '--target' to 'es2015' or newer

While attempting to enhance my Slider, I encountered an error when utilizing TypeScript React within this Next.js project: "Type 'IterableIterator' can only be iterated through when using the '--downlevelIteration' flag or with a ...

Youngster listens for guardian's occurrence in Angular 2

While the Angular documentation covers how to listen for child events from parents, my situation is actually the opposite. In my application, I have an 'admin.component' that serves as the layout view for the admin page, including elements such a ...

Solving Typing Problems in React TypeScript with "mui-icons" Props

I have a small compost project with only an App.JSX file that is functioning perfectly, but now I need to convert it to App.TSX. After changing the extension to .TSX, I encountered two errors that I'm unsure how to resolve. function MyComponentWithI ...

Encountering syntax errors with CommonJS Rollup plugin when attempting to import third-party libraries, particularly those involving the 'process' module

I have been developing a personalized rollup configuration that involves React projects and inlines the JS and CSS in index.html. When attempting to import third-party React libraries (such as material-ui-color), I encountered an issue with CommonJS repo ...

Guide to configuring a not null property in Typescript Sequelize:

Hello there! I am trying to figure out how to set a not null property using TypeScript Sequelize. I have tried using the @NotNull decorator, but unfortunately it does not seem to be working. The errors I am encountering are as follows: Validation error: W ...