Open mqyqingfeng opened 2 years ago
打卡
下面的函数体怎么写才能不报红?
下面的函数体怎么写才能不报红?
![]()
可能这么写
function createLabel<T extends string | number>(
idOrName: T
): NameOrId<T> {
if (typeof idOrName === 'number') {
return { id: 1 } as NameOrId<T>
} else if(typeof idOrName === 'string') {
return { name: '1' } as NameOrId<T>
}
}
下面的函数体怎么写才能不报红?
![]()
function createLabel(idOrName: number): IdLabel;
function createLabel(idOrName: string): NameLabel;
function createLabel(idOrName: number | string) : IdLabel | NameLabel {
if (typeof idOrName === 'number') {
idOrName
// ^?
return {
id: idOrName,
};
} else {
return {name: idOrName}
}
https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads
@kwSir-chen
下面的函数体怎么写才能不报红?
![]()
可能这么写
function createLabel<T extends string | number>( idOrName: T ): NameOrId<T> { if (typeof idOrName === 'number') { return { id: 1 } as NameOrId<T> } else if(typeof idOrName === 'string') { return { name: '1' } as NameOrId<T> } }
好粗暴,哈哈
@riskers
下面的函数体怎么写才能不报红?
![]()
function createLabel(idOrName: number): IdLabel; function createLabel(idOrName: string): NameLabel; function createLabel(idOrName: number | string) : IdLabel | NameLabel { if (typeof idOrName === 'number') { idOrName // ^? return { id: idOrName, }; } else { return {name: idOrName} }
https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads
这应该算是直接换了一种实现方式了吧,但是还是解释不了为什么采用extends的写法会报错
可以参考这个问题的回答
idOrName
是参数,可能是 string 也可能是 number,这是编译期不能决定的,所以没办法通过 NameOrId<T>
来约束返回值
@riskers
可以参考这个问题的回答
idOrName
是参数,可能是 string 也可能是 number,这是编译期不能决定的,所以没办法通过NameOrId<T>
来约束返回值
好的,感谢
@shjames 今天无意看到文档,可能会更权威回答你的问题: Distributive Conditional Types
其实文章最后也说过
在你的 case 里,NameOrId<string | number>
会被认为是 NameOrId<string> | NameOrId<number>
一个类型而已,为什么要搞得这么麻烦
@riskers
@shjames 今天无意看到文档,可能会更权威回答你的问题: Distributive Conditional Types
其实文章最后也说过
在你的 case 里,
NameOrId<string | number>
会被认为是NameOrId<string> | NameOrId<number>
所以还是只能用as Type 明确告诉编辑器返回类型是准确的,是这样吗
打卡~
前言
TypeScript 的官方文档早已更新,但我能找到的中文文档都还停留在比较老的版本。所以对其中新增以及修订较多的一些章节进行了翻译整理。
本篇整理自 TypeScript Handbook 中 「Conditional Types」 章节。
本文并不严格按照原文翻译,对部分内容也做了解释补充。
条件类型(Conditional Types)
很多时候,我们需要基于输入的值来决定输出的值,同样我们也需要基于输入的值的类型来决定输出的值的类型。条件类型(Conditional types)就是用来帮助我们描述输入类型和输出类型之间的关系。
条件类型的写法有点类似于 JavaScript 中的条件表达式(
condition ? trueExpression : falseExpression
):单从这个例子,可能看不出条件类型有什么用,但当搭配泛型使用的时候就很有用了,让我们拿下面的
createLabel
函数为例:这里使用了函数重载,描述了
createLabel
是如何基于输入值的类型不同而做出不同的决策,返回不同的类型。注意这样一些事情:string
,一个是number
),一个则是为了通用情况 (接收一个string | number
)。而如果增加一种新的类型,重载的数量将呈指数增加。其实我们完全可以用把逻辑写在条件类型中:
使用这个条件类型,我们可以简化掉函数重载:
条件类型约束 (Conditional Type Constraints)
通常,使用条件类型会为我们提供一些新的信息。正如使用 类型保护(type guards) 可以 收窄类型(narrowing) 为我们提供一个更加具体的类型,条件类型的
true
分支也会进一步约束泛型,举个例子:TypeScript 报错是因为
T
不知道有一个名为message
的属性。我们可以约束T
,这样 TypeScript 就不会再报错:但是,如果我们想要
MessgeOf
可以传入任何类型,但是当传入的值没有message
属性的时候,则返回默认类型比如never
呢?我们可以把约束移出来,然后使用一个条件类型:
在
true
分支里,TypeScript 会知道T
有一个message
属性。再举一个例子,我们写一个
Flatten
类型,用于获取数组元素的类型,当传入的不是数组,则直接返回传入的类型:注意这里使用了索引访问类型 里的
number
索引,用于获取数组元素的类型。在条件类型里推断(Inferring Within Conditional Types)
条件类型提供了
infer
关键词,可以从正在比较的类型中推断类型,然后在true
分支里引用该推断结果。借助infer
,我们修改下Flatten
的实现,不再借助索引访问类型“手动”的获取出来:这里我们使用了
infer
关键字声明了一个新的类型变量Item
,而不是像之前在true
分支里明确的写出如何获取T
的元素类型,这可以解放我们,让我们不用再苦心思考如何从我们感兴趣的类型结构中挖出需要的类型结构。我们也可以使用
infer
关键字写一些有用的 类型帮助别名(helper type aliases)。举个例子,我们可以获取一个函数返回的类型:当从多重调用签名(就比如重载函数)中推断类型的时候,会按照最后的签名进行推断,因为一般这个签名是用来处理所有情况的签名。
分发条件类型(Distributive Conditional Types)
当在泛型中使用条件类型的时候,如果传入一个联合类型,就会变成 分发的(distributive),举个例子:
如果我们在
ToArray
传入一个联合类型,这个条件类型会被应用到联合类型的每个成员:让我们分析下
StrArrOrNumArr
里发生了什么,这是我们传入的类型:接下来遍历联合类型里的成员,相当于:
所以最后的结果是:
通常这是我们期望的行为,如果你要避免这种行为,你可以用方括号包裹
extends
关键字的每一部分。TypeScript 系列
TypeScript 系列文章由官方文档翻译、重难点解析、实战技巧三个部分组成,涵盖入门、进阶、实战,旨在为你提供一个系统学习 TS 的教程,全系列预计 40 篇左右。点此浏览全系列文章,并建议顺便收藏站点。
微信:「mqyqingfeng」,加我进冴羽唯一的读者群。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。