typescript - 为什么以`T extends undefined`为条件的Typescript类型,用`boolean`实例化T,将T解析为`never`?
问题描述
下面的代码试图定义一个函数的类型,当它的泛型参数是 时不带参数调用undefined
,但对于任何其他参数类型都有 1 个参数。(可能有更好的方法来实现这一点,我很想在评论中看到链接,但问题是为什么 Typescript 的工作方式与我预期的不同。)
当T extends undefined
为假时,T
似乎变成了never
在 else 分支中,但仅在函数参数列表中...
type OptionalArgBroken<Arg> = Arg extends undefined ?
() => void :
(arg: Arg) => void;
const suppressArgBroken:OptionalArgBroken<undefined> = function() { };
suppressArgBroken(); // Fine
const haveArgBroken:OptionalArgBroken<boolean> = function(b:boolean) { };
haveArgBroken(true); // Type error
正如您在Playground上看到的,上面的最后一行给出了类型错误
“true”类型的参数不可分配给“never”类型的参数。(2345)
在阅读了https://github.com/microsoft/TypeScript/issues/31751之后,我尝试了 wrapping Arg
and undefined
in []
s,这似乎已经解决了问题:
type OptionalArgWorks<Arg> = [Arg] extends [undefined] ?
() => void :
(arg: Arg) => void;
const suppressArgWorks:OptionalArgWorks<undefined> = function() { };
suppressArgWorks(); // Fine
const haveArgWorks:OptionalArgWorks<boolean> = function(b:boolean) { };
haveArgWorks(true); // Fine
即使该修复有效,但这不是同一个问题:
type MakesSenseT = undefined extends undefined ? 'yes' : 'no'
const MakesSense:MakesSenseT = 'yes';
type ExtendsUndefined<T> = T extends undefined ? 'yes' : 'no'
const MakesSenseToo : ExtendsUndefined<undefined> = 'yes';
const MakesSenseThree : ExtendsUndefined<boolean> = 'no';
为什么我的原始代码不起作用?
解决方案
如所写,
type OptionalArgBroken<Arg> = Arg extends undefined ?
() => void :
(arg: Arg) => void;
是一个分布式条件类型,因为被检查的类型Arg
, 是一个裸泛型类型参数。
“分布式”意味着如果Arg
传入的是union,那么将分别为 union 的每个成员评估类型,然后再联合在一起(因此操作在 union 中分布)。换句话说,OptionalArgBroken<A | B | C>
将与 相同OptionalArgBroken<A> | OptionalArgBroken<B> | OptionalArgBroken<C>
。
这可能不是您的意图,这一点可以从您在包裹检查时对结果感到满意的事实证明[]
(这使得检查的类型不再通过“穿”它来“裸露”)。
此外,TypeScript 编译器将类型视为和的并boolean
集的简写,即所谓的布尔文字类型:true
false
type Bool = true | false;
// type Bool = boolean
如果您使用 IntelliSense 将鼠标悬停Bool
在 IDE 中,您将看到Bool
上面显示为boolean
.
boolean
如果您将其视为单一类型而不是其他两种类型的联合,这可能会令人惊讶。出现这种情况的一个地方是当您传递boolean
给分配条件类型时:OptionalArgBroken<boolean>
is OptionalArgBroken<true | false>
which is OptionalArgBroken<true> | OptionalArgBroken<false>
which is
type OABBool = OptionalArgBroken<boolean>;
// type OABBool = ((arg: false) => void) | ((arg: true) => void)
你传入了你认为是单一类型的东西,并得到了一个函数类型的联合。哎呀。(见微软/TypeScript#37279)
并且函数类型的联合只能通过其参数的交集安全地调用。阅读TS3.3 发行说明,了解支持调用函数联合的信息,了解为什么会这样。
但这意味着 type 的值OptionalArgBroken<boolean>
只能用 type 的参数调用,该参数true & false
被简化为never
(参见microsoft/TypeScript#31838),因为没有既是true
and的值false
。
因此,当您尝试调用时haveArgBroken
,它期望传入的参数类型为never
:
const haveArgBroken: OptionalArgBroken<boolean> = function (b: boolean) { };
// haveArgBroken(arg: never): void
并且true
不是 type never
,所以它失败了:
haveArgBroken(true); // Type error
这就是为什么您的原始代码不起作用的原因。
请注意,同样的事情发生在
type ExtendsUndefined<T> = T extends undefined ? 'yes' : 'no'
但它是良性的,因为ExtendsUndefined<boolean>
它ExtendsUndefined<true> | ExtendsUndefined<false>
被'no' | 'no'
简化为公正'no'
。它恰好是你想要的,但这只是因为没有办法区分'no'
that fromtrue
和 that come from false
。
好的,希望有帮助;祝你好运!
推荐阅读
- java - 我们如何在没有logstash和beats的情况下通过java将数据摄取到弹性搜索
- javascript - 在 javascript 中将时间表示为 CST - date-fns
- r - 多种颜色+改变geom_ribbon图的线条粗细
- algorithm - 为什么 Floyd Warshall 的算法会成功?
- azure-data-factory-2 - Azure 数据工厂聚合计数()
- go - 使用 Go 1.13 的 Google Cloud Function 上的“没有这样的文件或目录”
- php - 使用 JSON 文件而不是真正的数据库是不是很糟糕?
- html - 如何更改 Angular 10 中的 body 标签类(最佳实践)?
- ios - Swift - 在 JSON 文件中搜索特定值并获取同一数组中的整个字典数组
- jquery - dataTable - 设置导出文件名