Discovering potential types in Typescript through optional inference

Uncertain if the question is formulated correctly, but I am attempting to deduce the result type without knowing exactly how to achieve it.

This is my current approach:

type StateSetter<S> = (prevState: S) => S;
type ResolvableHookState<S> = S | StateSetter<S>;

export function resolveHookState<S>(state: S): S;
export function resolveHookState<S>(state: StateSetter<S>, currentState?: S): S;
export function resolveHookState<S>(state: ResolvableHookState<S>, currentState?: S): S {
  if (typeof state === 'function') {
    return (state as StateSetter<S>)(currentState as S);
  }

  return state;
}

This is how I intend to utilize it:

function someFunction(initialState: ResolvableHookState<number> = 0){
  const resolvedState = resolveHookState(initialState);
}

The objective is for resolveHookState(initialState) to return the number type, but currently it returns

ResolvableHookState<number>
due to certain constraints.

I attempted to achieve this through type inference, but the results were not as expected.

type ResolvableHookStateType<S extends ResolvableHookState<any>> = S extends StateSetter<infer T> ? T : S;

[UPD 1]
After some exploration, I arrived at the following solution:

export type StateSetter<S> = (prevState?: S) => S;

export type ResolvableHookState<S> = S | StateSetter<S>;

type ResolvableHookStateType<S> = S extends ResolvableHookState<infer T> ? T : never;

export function resolveHookState<S>(state: S): ResolvableHookStateType<S>;
export function resolveHookState<S>(state: StateSetter<S>, currentState?: S): ResolvableHookStateType<S>;
export function resolveHookState<S>(state: ResolvableHookState<S>, currentState?: S): ResolvableHookStateType<S> {
  if (typeof state === 'function') {
    return (state as StateSetter<S>)(currentState) as ResolvableHookStateType<S>;
  }

  return state as ResolvableHookStateType<S>;

Although effective, I am unsure if this is the most elegant way to address the issue.

Answer №1

interface Invokable<OutputType> {
  (...parameters: any[]): OutputType;
}

type ReturnedType<OutputType, F> = F extends Invokable<OutputType>
  ? OutputType
  : never;

function anotherFunction(initialValue: ResolvedState<number> = 0){
  const finalizedValue = ReturnedType<number, typeof initialValue>;
}

Check out this unique resource for more insights.

Answer №2

If you are comfortable with keeping the variable 'as' in place, then this solution will work effectively.

export type StateSetter<S> = (prevState?: S) => S

export type ResolvableHookState<S> = S | StateSetter<S>

export function resolveHookState<S>(
  state: ResolvableHookState<S>,
  currentState?: S
): S {
  if (typeof state === 'function') {
    return (state as StateSetter<S>)(currentState)
  } else {
    return state as S
  }
}

Answer №3

This code snippet demonstrates handling different types of states and state setters in TypeScript. The function `resolveHookState` resolves the state or state setter based on the input provided. It ensures that the state cannot be of type `Function` to avoid ambiguity in identifying the overload to handle.

type State<S> = Exclude<S, Function>;
type StateSetter<S> = (prevState: S) => S;
type ResolvableHookState<S> = State<S> | StateSetter<S>;

function resolveHookState<S>(state: State<S>): S;
function resolveHookState<S>(stateSetter: StateSetter<State<S>>, currentState: State<S>): S;
function resolveHookState<S>(stateOrStateSetter: ResolvableHookState<S>, currentState?: State<S>): S {
  if (typeof stateOrStateSetter === 'function') {
    if (currentState !== undefined) {
      const stateSetter = stateOrStateSetter as StateSetter<S>; 
      return stateSetter(currentState);
    } else {
      throw new Error('Oops?'); 
    }
  } else {
    const state = stateOrStateSetter;
    return state;
  }
}

// Call with state
resolveHookState(0);

// Or with setter
const stateSetter = (prevState: number) => prevState + 1;
resolveHookState(stateSetter, 5);

Answer №4

After studying @Grabofus's solution, I created my own version:

export type StateSetter<S> = (prevState: S) => S;
export type InitialStateSetter<S> = () => S;

export type InitialHookState<S> = S | InitialStateSetter<S>;
export type HookState<S> = S | StateSetter<S>;
export type ResolvableHookState<S> = S | StateSetter<S> | InitialStateSetter<S>;

export function resolveHookState<S>(newState: S | InitialStateSetter<S>): S;
export function resolveHookState<S>(newState: Exclude<HookState<any>, StateSetter<any>>, currentState: S): S;
export function resolveHookState<S>(newState: StateSetter<S>, currentState: S): S;
export function resolveHookState<S>(newState: ResolvableHookState<S>, currentState?: S): S {
  if (typeof newState === 'function') {
    return (newState as Function)(currentState);
  }

  return newState as S;
}

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

Is there a way to run the mediapipe face detection codepen.io demo on my laptop?

After successfully running the mediapipe face detection demo from Mediapipe official website, I wanted to replicate it on my laptop. To achieve this, I created an 'index.html' file and meticulously transferred the code from the CodePen demo page ...

Is it possible to pass multiple parameters in Angular by utilizing the click() function?

Is there a method for passing parameters with click() in Angular? <a asp-action="CreateSales" (click)="CreateSales(productname='pa', price='16.5')">Some Text</a> I am still learning Angular and would appreciat ...

Unexpected behavior: Promise.catch() fails to catch exception in AngularJS unit test

During the process of writing Jasmine unit tests for my Typescript app and running them via Resharper, I encountered an issue with executing an action when the handler throws an exception: describe("Q Service Test", () => { var q: ng.IQService; ...

Creating dynamic Angular child routes with variable initial segment

Recently, I've been working on a new project to set up a blogging system. The next step in my plan is to focus on the admin section, specifically editing posts. My idea for organizing the routes is as follows: /blog - Home page /blog/:slug - Access ...

Encountering a type mismatch error in Typescript while working with Redux state in a store file

It appears that I have correctly identified all the types, but could there be a different type of Reducer missing? 'IinitialAssetsState' is not assignable to type 'Reducer' The complete error message: Type '(state: { assets: n ...

Assign a variable to set the property of a class

Could something similar to this scenario be achievable? const dynamicPropName = "x"; class A { static propName = 1 // equivalent to static x = 1 } A[dynamicPropName] // will result in 1 or would it need to be accessed as (typeof A)[dynamicPropN ...

Cannot utilize remote.require() in TypeScript due to compatibility issues

Recently, I've been facing a frustrating issue while developing an Electron application in TypeScript. I've been trying to execute a module from a renderer process using the code snippet below: import { remote } from 'electron' const ...

Angular Error: Cannot call function panDelta on this.panZoomAPI

Check out my small demonstration using a stackblitz, I'm having an issue. In the setup, there's a master component with pan-zoom functionality containing a parent component with children content. The library in use is ngx-panzoom. The default c ...

How to Modify a Module that was Imported in Typescript

Apologies for my inexperience in this language. I've been working on a custom Discord bot and encountered a problem. I implemented the feature to load commands dynamically from a folder, with each command as a module. However, when trying to create a ...

A mistake has occurred: Unhandled promise rejection TypeError: Unable to assign the property 'devices' to an undefined object in Ionic 4 with Angular

Within my MyDevicesPage class, I am attempting to manipulate the res object and then pass it to the updateDevicesToServer method of DataService for further actions. The code compiles without errors, but at runtime, an error is thrown: ERROR Error: Uncaught ...

Enforcement of Class Initialization in Typescript 2.7

After initializing a sample project using the Angular template in Visual Studio 2017, I made sure to update the package.json file with the latest module versions. However, upon executing the npm install command and navigating to the site, an error related ...

Creating circular artwork with PixiJS: A step-by-step guide

I am trying to create a circular image with specific height and width dimensions, but have not found a satisfactory solution. Currently, I can achieve this using a texture, however it is drawn multiple times in the same position. const test = new Graphic ...

The error message states that the argument type '(src: Observable<any>) => Observable<any>' cannot be assigned to a parameter of type 'OperatorFunction<Object, any>'

Encountering a typescript error when trying to start the app. Not sure where I'm going wrong. It seems like it could be an issue with the rxjs version, but struggling to find the right solution. Seeing incompatible types on my system and not getting r ...

Tips on preventing the need for null or undefined checks in JS/Typescript singletons that have an initialization function

Is there a way to streamline the process of handling props in an Object literal that is dynamically initialized only once? I'm looking for a pattern that would eliminate the need for repetitive null/undefined checks and throw errors when certain metho ...

Determine data types for functions in individual files when using ElysiaJS

Currently, I am utilizing ElysiaJS to establish an API. The code can be found in the following open-source repository here. In my setup, there are three essential files: auth.routes.ts, auth.handlers.ts, and auth.dto.ts. The routes file contains the path, ...

Leveraging the power of React's callback ref in conjunction with a

I'm currently working on updating our Checkbox react component to support the indeterminate state while also making sure it properly forwards refs. The existing checkbox component already uses a callback ref internally to handle the indeterminate prop ...

Creating a Type that limits its keys to those from another Type, with the ability to assign new values to those keys. Attempting to introduce new keys should result in an

type Numbers = { a: number; b: number; f: number; }; type ValidateKeysWithDifferentTypes = SomeThingKeyOf<Numbers> & { a: string; b: Date; c: null; // Error occurs because 'c' is not found in Numbers type? // Error due ...

What is the proper way to create a React Context in TypeScript that accepts both a ref and a setState function as arguments?

When encountering various errors, one of them being: Type 'Dispatch<SetStateAction<null>>' is not assignable to type '() => void' My code looks something like this: import React, { ReactElement, ReactNode, useEffec ...

Error message: Deno package code encounters error due to the absence of 'window' definition

I am facing an issue with a npm package I imported into my Deno project. The code in the package contains a condition: if (typeof window === 'undefined') { throw new Error('Error initializing the sdk: window is undefined'); } Wheneve ...

summing 3 numbers to a total of 100 percent

I am currently trying to calculate the percentages of different statuses based on 3 count values. Let's assume I have 3 statuses: 1) Passed 2) Failed 3) Skipped When dealing with only two cases, I was able to use a combination of the Floor and Ceil ...