如何实现通用的第一个“案例/开关”类型

时间:2019-07-31 22:24:41

标签: typescript types

我目前正在重构redux-starter-kit的类型,以使非打字稿高级用户更容易阅读。

现在,它看起来像这样:

 type PayloadActionCreator<
  P = any,
  T extends string = string,
  PA extends PrepareAction<P> | void = void
  > =
  IfPrepareActionMethodProvided<PA,
    ActionCreatorWithPreparedPayload<PA, T>,
    IfMaybeUndefined<P,
      ActionCreatorWithOptionalPayload<P, T>,
      IfVoid<P,
        ActionCreatorWithoutPayload<T>,
        ActionCreatorWithPayload<P, T>
      >
    >
  >

但是我想消除嵌套,希望实现这样的东西:

type PayloadActionCreator<
  P = any,
  T extends string = string,
  PA extends PrepareAction<P> | void = void
  > =
  First<
    IfPrepareActionMethodProvided<PA, ActionCreatorWithPreparedPayload<PA, T>, void>,
    IfMaybeUndefined<P, ActionCreatorWithOptionalPayload<P, T>, void>,
    IfVoid<P, ActionCreatorWithoutPayload<T>, void>,
    ActionCreatorWithPayload<P, T>
  >;

因此,First应该是一种接受多种类型并返回非void的拳头的类型

我已经实现了First的一个简单版本,该版本工作得很好,直到将诸如IfVoid这样的泛型类型传递给它为止。然后,我得到一个返回void的情况,即使后面给出的选项不是void,也请参见以下代码:

type FallbackIfNotVoid<Type,  False> = [Type] extends [void] ? False : Type;

type First<
  A = void,
  B = void,
  C = void,
  D = void
  > = 
  FallbackIfNotVoid<A, 
  FallbackIfNotVoid<B, 
  FallbackIfNotVoid<C, 
  FallbackIfNotVoid<D, void>
  >>>;


  type IfVoid<P, True, False = void> = [void] extends [P] ? True : False;

  type Experiment<T> = First<
    IfVoid<T, "voidCase">,
    T
  >;

  type VoidType = Experiment<void>; // "voidCase", as expected
  type OtherType = Experiment<"test"> // this should be "test", but is void

我猜想这与TS进行了一些预优化有关,但事与愿违。 将鼠标悬停在类型上表示我的类型定义已优化为[void] extends [T] ? "voidCase" : void

有人对如何实现这种First类型有想法吗?

1 个答案:

答案 0 :(得分:2)

这是一个打字稿错误。

首先解决,如果我们从条件类型中取出void并将其放入类型别名的类型参数中:

type FallbackIfNotVoid<Default, Type, False> = [Type] extends [Default] ? False : Type;

type First<
  Default = void,
  A = void,
  B = void,
  C = void,
  D = void> =
  FallbackIfNotVoid<Default, A,
  FallbackIfNotVoid<Default, B,
  FallbackIfNotVoid<Default, C,
  FallbackIfNotVoid<Default, D, Default>>>>;


type IfVoid<P, True, False = void> = [void] extends [P] ? True : False;

type Experiment<T, Default = void> = First<Default,
  IfVoid<T, "voidCase">,
  T
>;

type VoidType = Experiment<void>; // "voidCase", as expected
type OtherType = Experiment<"test"> // "test" now

现在该错误,这是最小复制:

type Default = { x: string }

type M<A> = [A extends "A" ? A : Default] extends [Default] ? "Y" : "N" // Simplified to "Y" regardless of A

M简化为"Y"。如果测试类型([A extends "A" ? A : Default])也是条件类型,并且该条件类型在false分支中包含外部条件类型中要测试的类型,则会发生这种情况。这仅在false分支上发生。例如,以下类型不会得到类似的简化:

type Default = { x: string }

type M<A> = [A extends "A" ? Default: A] extends [Default] ? "Y" : "N" // no simplification

在类型参数中放入Default可以解决此问题,因为它可以防止这种急切的优化。

我将搜索一个现有问题并将其发布。我100%确信我已经看到过与条件类型简化有关的类似问题,只是不确定它们是否正是这种问题。

编辑:是的,我是对的,以前我确实看过类似的东西,甚至参与了有关它的谈话(我年老时会健忘?)Here,杰克威廉斯也有类似的复制品:

type Inner<T> = [T] extends [true] ? false : true;
type Outer<T> = [Inner<T>] extends [true] ? 1 : 2; // Already resolved to 2.