Create a function with an optional argument when the type can be inferred as an empty object

Is there a way to enforce a function's argument to be required if it has keys in the inferred type, and optional if it does not exist or is an empty object?

I've experimented with conditionals and overloads but have yet to find a solution.

const MyFunc = <T extends {}>(vars?: T): void => {
  // ...
}

// Should be allowed
MyFunc()

// Should be allowed
MyFunc<{}>()

// Should not be allowed - the argument should be required
MyFunc<{ a: string }>()

I've tried using overloads and conditional types like keyof T extends never ? ..., but I am stuck. Is this achievable with current TypeScript features?

Thank you!

Answer №1

If you want to always have MyFunc as a generic function with only one type parameter T corresponding to the type of the vars argument, then the change should be made in the parameter types of your call signature. If you decide to relax that restriction, using overloads and other methods might achieve similar effects, but it will lead to a different question.

The solution I propose is as follows:

const MyFunc = <T extends {}>(
   ...args: {} extends T ? [vars?: T] : [vars: T]
): void => { }

In this solution, instead of having a single vars parameter, we use an args rest parameter. The type of this rest parameter is a tuple type that depends on whether an empty object type can be assigned to T. If yes, then the tuple contains a single optional element of type T</code, making <code>vars optional. If not, the tuple includes a single required element, making vars mandatory.

You can test it with the following examples:

MyFunc(); // valid
MyFunc<{}>(); // valid
MyFunc<{ a: string }>(); // error!
MyFunc<{ a?: string }>(); // valid

This implementation works correctly. It's worth noting that since {} can be a valid value for {a?: string}, calling MyFunc<{a?: string}>() is allowed.

Link to Playground for Testing

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 the steps to extract information from an observable?

Having trouble retrieving data from a request? I've encountered an issue where the data retrieved inside .subscribe in an observable function is returning as undefined when trying to access it outside the function. It's quite frustrating! Here i ...

Selecting the appropriate color code based on a specified value

If the value of zoneTempDiff1 falls below 1.5, consider using temp: 1 color. If it exceeds 1.5, opt for temp: 2 color. The same logic applies to all different values such as -1, -2, 0, 1, 2, 3, 4, or 5, each corresponding to a specific color code. In cas ...

Creating a Jest mock object in Typescript: A step-by-step guide

Currently, I am working on a jest test where my main goal is to simulate a service returning an object that aligns with its interface. The object in question has quite a complex constructor, and manually creating it would be laborious while ensuring TypeSc ...

Best practice in Typescript for "Incompatible types for parameters 'X' and 'X'"

Can someone help me with a Typescript question I have? demo-link // Looking for the best way to handle Params and Options in this scenario interface Params { name: string; age: number; } interface Option { callback: (params: Params | Params[]) ...

Are there any alternatives to the Safe Navigation Operator specifically for TypeScript files with versions less than 3.7?

After updating my TypeScript to version 3.2, I found myself in need of an alternative to the safe navigation operator. It makes my code very lengthy when I have to check for 3 to 4 keys. For example, if I need to check for Obj.key1.key2,key3, then my code ...

Generating TypeScript declarations for legacy CommonJS dependencies with the correct "module" setting

One of my challenges involves creating type declarations for outdated dependencies that produce CJS modules and lack typings. An example is the aabb-3d module (although this issue isn't specific to that particular module). To generate the declaration ...

Enhance all React/Html elements with a custom property in React using TypeScript

While attempting to implement a new custom property for all HTML elements, I am encountering the following issue: index.js:1350 Warning: Unknown event handler property `onTap`. It will be ignored I have created an index.d.ts file and included the followin ...

Prevent TypeScript from generalizing string literals as types

I have a constant Object called STUDY_TAGS with specific properties const STUDY_TAGS ={ InstanceAvailability: { tag: "00080056", type: "optional", vr: "string" }, ModalitiesinStudy: { tag: "00080061", type: " ...

What is the best method for transferring data to Angular routed components?

Within one of my Angular 2 routes templates, specifically in FirstComponent, there is a button present. first.component.html <div class="button" click="routeWithData()">Pass data and navigate</div> The main objective here is to: When the ...

Is there a way to ensure my custom tslint rule is compatible with the exact version of the TypeScript module being used by tslint?

I seem to be missing something crucial, but I can't pinpoint the issue. Within my custom rule, I am utilizing the SyntaxKind of a Node for controlling my flow, as shown below: import * as ts from "typescript" function processPropertyName(pn: ts.Pro ...

Solving the issue of interconnected promises in Angular services

I am utilizing a DynamoDB service within my Angular project which returns a promise through a series of promises. This process involves retrieving a subId from Cognito and then passing that subId to a DynamoDB get query: async getUserObject(): Promise< ...

Can someone explain how I can extract values from components that have been added using a for each loop in Svelte

Embarking on my journey in frontend development and Svelte, I decided to dive into some experimentation. Here is a counter component with buttons to increment or decrement the number of items: Counter.svelte <script> export let quantity = 0; ...

Having trouble utilizing a JavaScript file within TypeScript

I am currently exploring the integration of Three.js into an Angular application. Following the documentation, I imported Three.js using the following line: import * as THREE from 'three'; In addition, I installed the types for Three.js with th ...

Encountering the issue: "Property '...' is not found on the type 'typeof "...."'"

Currently, I am in the process of developing a node js application using typescript. To transpile the code, I am utilizing the gulp transpiler in commonjs mode. One of the files I've written is homeController.ts, which looks like this: let homeContr ...

React-Bootstrap columns are not displaying in a side by side manner and are instead appearing on separate lines

I am currently integrating Bootstrap into my React project alongside Material UI components. Below is a sample of one of my components: import { styled } from "@mui/material/styles"; import Paper from "@mui/material/Paper"; import Cont ...

Error: Unable to attach the "identity" property as the object does not support extension

I encountered a simple TypeError while attempting to format my POST body. Below is the function I am using for handleSubmit : const handleSubmit = (values: any, formikHelpers: FormikHelpers<any>) => { const prepareBody = { ...values.customerC ...

Create a Node.js Express app that retrieves data using a GET request from an array

I am facing a challenge with my folder containing JSON files and my backend code. I want to access the JSON files dynamically without having to manually update the data each time. The issue I'm encountering is as follows: localhost/Foo.json Prints { ...

Comparing TypeScript's `return;` with `return undefined;`: which is better?

I encountered a strange behavior that is puzzling to me, and I'm not sure if there's a logical explanation for it. In TypeScript, the following code works perfectly: type UndefinedFunction = () => undefined; let uf: UndefinedFunction = funct ...

Immutable parameter in constructor

While analyzing some TypeScript code, I stumbled upon a peculiar declaration within a class definition: constructor(readonly constructorParam : Type) { // no assignment of constructorParam here } Surprisingly, constructorParam is still being used as usu ...

Resolve the issue pertaining to the x-axis in D3 JS and enhance the y-axis and x-axis by implementing dashed lines

Can anyone assist with implementing the following features in D3 JS? I need to fix the x-axis position so that it doesn't scroll. The values on the x-axis are currently displayed as numbers (-2.5, -2.0, etc.), but I want them to be shown as percentag ...