【问题标题】:Typescript optional type inferringTypescript 可选类型推断
【发布时间】:2019-10-31 07:54:50
【问题描述】:

不确定问题是否正确,但我希望它推断结果类型但实际上不知道该怎么做。

我现在是怎么做的:

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&lt;number&gt;

我试图通过推断来实现,但它似乎没有像我预期的那样工作。

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

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

【问题讨论】:

  • 小心as 关键字的使用,它只是告诉TypeScript 闭嘴.. :) 如果我想在我的状态下存储函数会发生什么(这是你第一个忽略的as)?如果我用 stateSetter 调用 resolveHookState 而没有当前状态(第二个 as)怎么办?
  • @Grabofus 我正在制作 custorm React 钩子,它应该被用作它的原生 useState,它的参数可以是一个值或返回一个值的函数,如果它是一个调用它的函数没有参数,它的结果用作状态。在这一部分中,它的作用与反应完全一样(如果你想存储一个函数,你必须用另一个函数包装它)。
  • @Grabofus 此外,react 的 useState 钩子返回一个可以设置新状态的函数。它也可以是一个值或函数,但这个时间函数将接收 1 个参数 - 当前状态值。因此,如果我是对的(我希望我是 %))我创建了一个涵盖两个用例的函数。这个函数不会暴露给用户,所以有效的用例会被测试捕获
  • 唯一的问题是,如果&lt;S&gt; 是函数类型(但不是StateSetter),您将调用它,而不是将其保存为状态。请在下面查看我的答案,看看它是否适合您的情况。 :)
  • 编辑:删除了答案,似乎有一些问题..如果您没有找到解决方案,我会重新访问并尽快更新

标签: typescript typescript-generics


【解决方案1】:
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的文章或许能帮到你。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 非常感谢!根据您的解决方案,我制作了一个完全符合我需求的解决方案。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-03
        • 2019-09-17
        • 1970-01-01
        • 1970-01-01
        • 2017-10-04
        • 1970-01-01
        • 2016-04-04
        相关资源
        最近更新 更多