Creating a TypeScript function that utilizes generics to automatically infer the return type

How can I create a function with a generic argument that can return any type, and have the return type inferred from its usage?

I attempted the following code:

type Thing<T> = <U>(value: T) => U

const shouldMakeStrings: Thing<string> = (a: string) => a + 'foo'
const shouldMakeObjects: Thing<string> = (a: string) => ({ foo: a })

I expected the types of shouldMakeStrings to be (a: string) => string and shouldMakeObjects to be

(a: string) => { foo: string }

However, I encountered an error stating that

'U' could be instantiated with an arbitrary type which could be unrelated to 'string'

Is there a way to define the Thing type without specifying U, but still allow for any return type?


Below is a more comprehensive example demonstrating what I'm trying to achieve (playground link):

type Thing<T> = <U>(value: T) => U

// Example, this will be a big map of custom types and string representations
type Types = {
  'string': string,
  'number': number
}

type CallbackMap = {
  [P in keyof Types]?: Thing<Types[P]>
}

const callbacks: CallbackMap = {}

const addCallback = <V>(type: keyof Types, callback: Thing<V>) => {
  callbacks[type] = callback
}

addCallback('string', (a: string) => a + 'foo')
addCallback('number', (a: number) => { foo: a })

callbacks.string // = callback that takes a string and returns something, with it's type inferred as string from `addCallback('string')` above
callbacks.number // = callback that takes a number and returns something, with it's type inferred as '{ foo: number }' from `addCallback('number')` above

Here's a more verbose example, although it has other TypeScript-related issues (playground):

type A = { foo: string }
type B = { bar: string }
type C = number[]

type Modifiable = {
  'typeA': A,
  'typeB': B,
  'typeC': C
}

type Modifier<T> = <U>(value: T) => U
type ModifierMap = {
  [P in keyof Modifiable]?: Modifier<Modifiable[P]>
}

const modifiers: ModifierMap = {
  'typeA': (a: A) => a
}

const setModifier = <V extends keyof Modifiable = keyof Modifiable>(type: V, callback: Modifier<Modifiable[V]>) => {
  modifiers[type] = callback
}

setModifier('typeA', (input: A) => input.foo + 'a')

setModifier('typeA', a => a.foo)

// With the aim to be able to do something like this:
const makeModifiedA = (modifiers?: ModifierMap) => {
  const a = { foo: "hello world" }

  return modifiers?.typeA ? modifiers.typeA(a) : a
}

makeModifiedA(modifiers) // = "hello world"
setModifier('typeA', (input: A) => ({ ...input, upperFoo: input.foo.toUpperCase() }))
makeModifiedA(modifiers) // = { foo: "hello world", upperFoo: "HELLO WORLD" }

Answer №1

When using TypeScript, you have the choice to assign a type or let it be inferred, but not both simultaneously.

If you want to ensure that a function meets certain criteria without affecting its type inference, you can employ a workaround. This involves performing a constraint check before the type is finalized.

const satisfies = <Constraint>() => <Input extends Constraint>(input: Input) => input;

const checked = satisfies<(arg: string) => any>()(arg1 => {
  console.log(arg1);
  return 2;
});

Interestingly, there is an ongoing proposal to introduce this functionality as a built-in feature in TypeScript known as the satisfies operator.

https://github.com/microsoft/TypeScript/issues/47920

Therefore, in future versions of TypeScript (4.9 and beyond), you may see syntax like:

const checked = ((arg1: string) => 2) satisfies (arg: string) => any

Answer №2

Is it possible for any/unknown to be a match?

type Item<T> = (data: T) => unknown

const generateStrings: Item<string> = (str: string) => str + 'bar'
const generateObjects: Item<string> = (str: string) => ({ bar: str })

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

Error in Typescript SPFx: The property 'news' is not found in the type 'Readonly<{}>'

Currently, I am working on developing a spfx react component to showcase an RSS feed in the browser. My prototype is functional in a test environment, however, as spfx utilizes TypeScript, I am encountering a type error that I am unsure how to resolve. Rs ...

What does the concept of "signaling intent" truly signify in relation to TypeScript's read-only properties?

Currently diving into the chapter on objects in the TypeScript Handbook. The handbook highlights the significance of managing expectations when using the readonly properties. Here's a key excerpt: It’s crucial to clarify what readonly truly signif ...

Angular does not completely erase everything

Having some issues with file deletion while working on angular and typescript. My setup involves three interfaces: Project, SubProject, and Position. When a subproject is added to the selected project, it gets included in the subProjectIds list of the Proj ...

Ensuring consistency between TypeScript .d.ts and .js files

When working with these definitions: https://github.com/borisyankov/DefinitelyTyped If I am using angularJS 1.3.14, how can I be certain that there is a correct definition for that specific version of Angular? How can I ensure that the DefinitelyTyped *. ...

Testing the subscribe function in Angular within a returned Promise: A guide

I am facing an issue with a service that returns a Promise. It retrieves data from a JSON file using a subscribe method before resolving the Promise. I am trying to test the result of this Promise based on the parameters I provide, but I am encountering t ...

Using Angular template to embed Animate CC Canvas Export

Looking to incorporate a small animation created in animate cc into an angular template on a canvas as shown below: <div id="animation_container" style="background-color:rgba(255, 255, 255, 1.00); width:1014px; height:650px"> <canvas id="canv ...

Tips for adding a time increment of 24 hours to a date variable in Angular 6

My goal is to update a date variable called EndDate stored in localStorage by adding exactly 24 hours to it. The current value in the localStorage is Sun Jun 09 2019 20:39:44 GMT+0530 (India Standard Time). var endDate = new Date(); endDate.setDat ...

Understanding the significance of emitDecoratorMetadata in transpiled code

I have a question regarding the significance of the emitDecoratorMetadata option when transpiling TypeScript to JavaScript in an Angular 2 environment. If this option is set to false, and metadata is not included in the final code, what impact will it ha ...

Show just a single error message if there are two validation errors present

In my AngularJS timepicker, users can choose multiple time segments for each day. The code has validation to detect duplicates and overlapping time segments. For example, entering 11:00am - 12:00am twice will trigger two error messages: 'Overlapping t ...

How can I find the "types" specific to modules within the "firebase" library?

I have a question that applies to various scenarios, with Firebase serving as an example. When working on my react project, I find myself wanting to import firebase from "@firebase/app", which is logical. However, if I want the const locationRef ...

The static side of the class `typeof _Readable` is erroneously extending the static side of the base class `typeof Readable`

I am currently developing a Discord bot using node/typescript. After running the typescript compiler on my code, I encountered this error: node_modules/@types/readable-stream/index.d.ts(13,15): error TS2417: Class static side 'typeof _Readable' ...

In order to showcase the data from the second JSON by using the unique identifier

SCENARIO: I currently have two JSON files named contacts and workers: contacts [ { "name": "Jhon Doe", "gender": "Male", "workers": [ "e39f9302-77b3-4c52-a858-adb67651ce86", "38688c41-8fda-41d7-b0f5-c37dce3f5374" ] }, { "name": "Peter ...

Unable to resolve the Typescript module within a different file

I am in the process of transitioning my React app to TypeScript. Currently, everything is working fine. However, I encountered an issue after adding the TypeScript compiler and renaming files to .ts and .tsx extensions - it is now throwing a "module not fo ...

When utilizing Angular 2, this message is triggered when a function is invoked from the Observable

One of my services is set up like this: @Injectable() export class DataService { constructor(protected url: string) { } private handleError(error: Response) { console.log(this.url); return Observable.throw(new AppError(error)); ...

The ListItemButton's onclick event does not trigger on the initial click when utilizing a custom component as its children

I am having trouble comprehending why this onclick function is influenced by the children and how it operates <ListItemButton onClick={() => onClickResult(q)}> <Typography variant="body1">{highlighted}</Typography> ...

When is the right time to develop a Node.js application using Typescript with dockerization

Currently, I am developing a full stack TypeScript application using Express for the server and React for the client. The folder structure of my project is organized as shown below: . ├──client/ <-- React app ├──server/ <-- Express serve ...

In Javascript, check if an item exists by comparing it to null

I am working with a dropdown list that can be used for various types of data. Some of the data includes an isActive flag (a BOOLEAN) while others do not. When the flag is absent, I would like to display the dropdown item in black. However, if the flag exis ...

Inferencing partial types in Typescript

I'm completely stuck on this and can't seem to figure it out without using a second function: interface Fixed { a: number } const fn = <A, B extends {} = {}>(b: B) => { return b } fn({ a: 1 }) // { a: number } fn<Fixed>({ a: 1 } ...

Best practice for incorporating the cq-prolyfill third-party JavaScript library into an Angular 5 application

I'm experiencing an issue with the cq-prolyfill library not functioning properly when included through a typescript import statement within an angular module. I have confirmed that it is included in my vendor bundle, but for some reason the initial se ...

Encounter the "Error: Source 'cloudsTileLayer-RasterSource' not found" message while trying to integrate a weather tile layer into Azure Maps

I have been working on a React application that utilizes the React-Azure-Maps npm package. My current challenge involves creating a weather layer, which I believe shares similarities with the sample code provided for layers. The code snippet responsible f ...