Implying generics at a later time, not during instantiation / altering the type of a generic

If we consider the example provided, is there a way to instruct the typescript compiler that the return type of baz must be string, since it can be inferred from foo.a('aString') that it's a string?

const fn = <T,S>()=>{
  let s: S
  let t: T
  return {
    a: <Z extends T=T>(arg: Z)=>{
      t = arg
      return s
    },
    b: <V extends S=S>(arg:V)=>{
      s = arg
      return t
    }
  }
}
const foo = fn()
const bar = foo.a('aString')
// currently,baz's type is 'unknown', but it is determinable that the type should be 'string'
const baz = foo.b('aString') 

code

A solution is to define

const foo = fn<string, string>()
. However, this requires one to specify both types upfront when foo.a('aString') may be the best place to infer T, rather than having to explicitly specify the type.

I suspect not ... but one can have hopium!

Answer №1

fn functions simulate the behavior of a class. TypeScript provides strong support for classes, so it is recommended to utilize class in your code examples. Take a look at this implementation:

class fn<T, S>{
  s: S | undefined = undefined
  t: T | undefined = undefined

  constructor() { }

  a<Z extends T = T>(arg: Z): asserts this is this & { t: Z } {
    this.t = arg
  }
  b<V extends S = S>(arg: V): asserts this is this & { s: V } {
    this.s = arg
  }

}
const foo: fn<string, string> = new fn<string, string>()

foo.a('aString')
foo.t // 'aString'

foo.b('bString')
foo.s // 'bString'

Interactive Playground

If you modify this within your class and need to track those changes, you must use assertion functions.

However, this approach won't work if you call the fn function without explicitly specifying generic arguments. In such cases, TypeScript defaults to using unknown as the type.

Consider the following example:

const bar = <T,>() => {
  return 42 as any as T
}
//const bar: <unknown>() => unknown
bar()

The T type cannot be inferred because no value was provided. The same issue occurs with the fn function as well.

Answer №2

Here is a clever workaround:

const func = <T,S>(t?: T, s?: S)=>{
  return {
    a<NewT>(arg: NewT) {
      return func<NewT,S>(arg,s)
    },
    b<NewS>(arg: NewS) {
      return func<T,NewS>(t,arg)
    },
    t(){
      return t as T // changed for simplicity
    },
    s(){
      return s as S // changed for simplicity
    }
  }
}
const example1 = func()
const example2 = example1.a('aString')
const result1 = example2.t() // string
const result2 = example2.s() // unknown
const example3 = example1.b('aString') 
const result3 = example3.t() // unknown
const result4 = example3.s() // string
const finalExample = example2.b('aString') 
const result5 = finalExample.t() // string
const result6 = finalExample.s() // string

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

Identifying Data Types in Typescript Using a Union Type Guard

I came across this interesting type guard: enum X { A = "1" } const isNullableX = (value: any): value is X | null => false let bar: string | null = '' if (isNullableX(bar)) { console.log(bar) } Surprisingly, in the last con ...

Determine the character count of the text within an *ngFor loop

I am a beginner in Angular (8) and I am trying to determine the length of the input value that I have created using a *ngFor loop as shown below: <div *ngFor="let panel of panels; index as i" class="panel" [id]="'panel-' + panel.id"> & ...

Jasmine: utilizing unit test to spy on the invocation of a nested function

When running unit tests for an Angular app, I need to spy on a function called func1 to check if it is being called. However, within func1 there is a call to func2 and I also want to spy on that to see if it is being called. How should I structure my unit ...

Create a system for detecting changes in simple input elements and triggering a function to update the final result

There are a maximum of 12 inputs that represent the same entities or objects but with varying integer values. These values directly impact the final result displayed to the user. Whenever any of the input values change, a function needs to be triggered to ...

Angular service providing components (TypeScript error)

This is my first time trying to dynamically inject components and so far, I've been successful. However, there's an error in Typescript that's bothering me (I don't like having errors in my code). If you want to check out the app, here ...

What is the reason behind the never return value in this typescript template?

As demonstrated in this example using TypeScript: Access TypeScript Playground type FirstOrSecond<condition, T1, T2> = condition extends never ? T1 : T2 type foo = never extends never ? () => 'hi' : (arg1: never) => 'hi' ...

What is the best way to convert a tuple containing key/value pairs into an object?

How can the function keyValueArrayToObject be rewritten in order to ensure that the type of keyValueObject is specifically {a: number; b: string}, instead of the current type which is {[k: string]: any}? const arrayOfKeyValue = [ {key: 'a', val ...

Using Typescript generics within a callback function

I am currently working on developing a versatile service that can fetch data from a remote source and create objects based on that data. @Injectable() export class tService<T> { private _data: BehaviorSubject<T[]> = new BehaviorSubject([]) ...

Tips for sorting queries within a collection view in Mongoose:

I am working with Mongoose and creating a view on a collection. NewSchema.createCollection({ viewOn: originalModel.collection.collectionName, pipeline: [ { $project: keep.reduce((a, v) => ({ ...a, [v]: 1 }), {}), }, ], ...

Electron window widens but does not shrink in size

Currently, I am utilizing electron to create an application where I am using ipc messages to expand and retract the width of the app. The frontend is able to trigger these ipc messages successfully, but for some reason, it refuses to go back to a width of ...

Ensuring that the app closes completely before launching a new instance with webpack hot module reload

My nest.js application is utilizing webpack hot module reload (hmr), but I am encountering an issue where the reload does not allow the old instance to fully close (including the database connection and telegram bot) before launching a new instance. This c ...

Showing canvas lines while dragging (using only plain JavaScript, with React.JS if needed)

Is there a way to add lines with two clicks and have them visible while moving the mouse? The line should only be drawn when clicking the left mouse button. Any suggestions on how I can modify my code to achieve this functionality? Currently, the lines are ...

What would cause the nsfw property to be absent from a TextChannel in client.on("messageCreate") event?

Currently working with Typescript in combination with Discord.js v14, I'm encountering the following error: Property 'nsfw' does not exist on type 'DMChannel | PartialDMChannel | ... Below is the snippet of problematic code: client.on( ...

Utilizing WebPack 5 in conjunction with Web workers in a React/Typescript environment

Can someone help me figure out how to make a web worker function properly with create-react-app, Typescript, and Webpack 5? I've been struggling with limited documentation and can't seem to find a clear explanation. I'm trying to avoid using ...

Setting up TypeScript in an Angular 2 project and integrating Facebook login

Currently, I am in the process of familiarizing myself with Angular 2 and typescript. Although things have been going smoothly so far, I have hit a roadblock while attempting to implement a Facebook login message. In my search for a solution, I stumbled up ...

Exploring the nesting of client components in Next.jsIf you are

Exploring the realm of NextJS and React, I find myself delving into the realm of client components. One such client component I'm working with is called Form.jsx. It looks something like this: export default function FormHome() { ... a plethora of ...

The map function is calling an unresolved function or method named "map"

I'm encountering an error with the map method in my code, even after correctly importing 'rxjs/add/operator/map'. I've followed all the necessary steps and upgraded to rxjs 5.0.1, but the error persists. Do you have any suggestions on h ...

What could be causing the presence of a "strike" in my typescript code?

While transitioning my code from JavaScript to TypeScript for the first time, I noticed that some code has been struck out. Can someone explain why this is happening and what it signifies? How should I address this issue? Here's a screenshot as an exa ...

Using async method in controller with NestJS Interceptor

I am seeking a way to capture both the result and any potential errors from an asynchronous method within a controller using an interceptor. When an error is thrown, the interceptor can respond accordingly. However, I am struggling to figure out how to tri ...

What is the best way to fetch all Firebase database IDs using Angular?

Is there a way to fetch all data from Firebase database along with their respective IDs? Currently, I have two functions - getAll() and get(input) that retrieve specific products based on the given ID. However, my current implementation only returns obje ...