简体   繁体   English

Typescript 可选类型推断

[英]Typescript optional type inferring

Not sure if question is right, but I want it to infer the result type but actually have no clue how to do it.不确定问题是否正确,但我希望它推断结果类型但实际上不知道如何去做。

Here how i did it now:我现在是怎么做的:

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;
}

Here is how i want to use it:这是我想使用它的方式:

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

Idea is to make resolveHookState(initialState) return the number type, but for obvious reasons it returns ResolvableHookState<number>想法是让resolveHookState(initialState)返回数字类型,但由于显而易见的原因,它返回ResolvableHookState<number>

I've tried to make it via infering but it seems not to work as i expect.我试图通过推断来做到这一点,但它似乎没有像我预期的那样工作。

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

[UPD 1] [更新 1]
After some tinkering ended up with this:经过一番修修补补,结果如下:

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>;

But i'm not sure it is the most elegant way to solve the problem.但我不确定这是解决问题的最优雅的方法。

interface Callable<ReturnType> {
  (...args: any[]): ReturnType;
}

type GenericReturnType<ReturnType, F> = F extends Callable<ReturnType>
  ? ReturnType
  : never;

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

This article might be able to help you. 这篇文章或许能帮到你。

If you're fine with keeping as around, this works如果您可以保持as ,这可行

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
  }
}

This is the closest I could get.这是我能得到的最接近的。 One thing to keep in mind is your state cannot be of type Function otherwise you wouldn't be able to tell which overload you're trying to handle.要记住的一件事是您的 state 不能是Function类型,否则您将无法判断您正在尝试处理哪种过载。

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) {
      // From: StateSetter<S> | (Exclude<S, Function> & Function)
      // Not sure why (Exclude<S, Function> & Function) doesn't resolve to never
      const stateSetter = stateOrStateSetter as StateSetter<S>; 
      return stateSetter(currentState);
    } else {
      // If you call resolveHookState with a stateSetter and no currentState
      // This should not be possible based on the method signatures.
      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);

Basing on @Grabofus solution made my own:基于@Grabofus 解决方案制作了我自己的:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM