typescript - 如何实现通用的第一个“case/switch”类型
问题描述
我目前正在重构 redux-starter-kit 的类型,使其对非 typescript-poweruser 更具可读性。
现在,它看起来像这样:
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
它工作得很好,直到一个带有泛型 like 的类型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
类型有想法吗?
解决方案
这是一个打字稿错误。
首先是解决方法,如果我们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 分支中包含在外部条件类型中测试的类型,则会发生这种情况。这只发生在假分支上。例如,下面的类型没有得到类似的简化:
type Default = { x: string }
type M<A> = [A extends "A" ? Default: A] extends [Default] ? "Y" : "N" // no simplification
将Default
放入类型参数可以解决此问题,因为它可以防止这种急切的优化。
如果我找到它,我将搜索现有问题并将其发布。我 100% 确定我已经看到与条件类型简化相关的类似问题,只是不确定它们是否正是这个问题。
编辑:是的,我是对的,我之前确实看到过类似的东西,甚至参与了有关它的对话(在我年老时变得健忘)这是问题所在,jack-williams 也有类似的复制品:
type Inner<T> = [T] extends [true] ? false : true;
type Outer<T> = [Inner<T>] extends [true] ? 1 : 2; // Already resolved to 2.
推荐阅读
- oracle - 如何让 Oracle 12c 不审计 SYS 拥有的数据库触发器操作在体内执行?
- java - if 语句不遵守的规则
- c++ - 在 C++ 程序中找不到环境变量 UID
- ruby-on-rails - Rails 并发问题锁定
- javascript - 加载图像路径以使用图像 ReactJS 创建组件
- node.js - 是否可以编辑 JWT 令牌的过期时间?
- angular - Angular RxJS 过滤器 - 返回一个空的或填充的 Observable 列表
- python - 是否可以将以一种方式堆叠的 Python 数组重塑为另一种堆叠类型?
- ghostscript - 重新调整整个 EPS 文件帧边界
- reactjs - React 元素未呈现/React 设置问题