Typescript可选类型推断

时间:2019-10-30 20:26:42

标签: typescript typescript-generics

不确定问题是否正确,但我希望它推断结果类型,但实际上不知道如何执行。

这是我现在的操作方式:

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

这是我要使用的方式:

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

想法是使resolveHookState(initialState)返回数字类型,但是出于显而易见的原因,它返回ResolvableHookState<number>

我试图通过推断来做到这一点,但它似乎没有按我预期的那样工作。

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

[UPD 1]
经过一番修补后,最终结果是:

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

但是我不确定这是解决问题的最优雅的方法。

4 个答案:

答案 0 :(得分:0)

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

https://reacttraining.com/react-router文章可能会为您提供帮助。

答案 1 :(得分:0)

如果您可以保留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
  }
}

答案 2 :(得分:0)

这是我能得到的最接近的。要记住的一件事是您的状态不能为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);

答案 3 :(得分:0)

基于@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;
}