这篇文章主要讲解了“TypeScript条件类型怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“TypeScript条件类型怎么使用”吧!
创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都做网站、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的淄博网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
当 extends 用于表示条件判断时,可以总结出以下规律
若位于 extends 两侧的类型相同,则 extends 在语义上可理解为 ===,可以参考如下例子:
type result1 = 'a' extends 'abc' ? true : false // false type result2 = 123 extends 1 ? true : false // false
若位于 extends 右侧的类型包含位于 extends 左侧的类型(即狭窄类型 extends 宽泛类型)时,结果为 true,反之为 false。可以参考如下例子:
type result3 = string extends string | number ? true : false // true
当 extends 作用于对象时,若在对象中指定的 key 越多,则其类型定义的范围越狭窄。可以参考如下例子:
type result4 = { a: true, b: false } extends { a: true } ? true : false // true
考虑如下 Demo 类型定义:
type Demo= T extends U ? never : T
结合用于条件判断时的 extends,可知 'a' | 'b' | 'c' extends 'a' 是 false, 因此 Demo<'a' | 'b' | 'c', 'a'> 结果是 'a' | 'b' | 'c' 么?
查阅官网,其中有提到:
When conditional types act on a generic type, they become distributive when given a union type.
即当条件类型作用于泛型类型时,联合类型会被拆分使用。即 Demo<'a' | 'b' | 'c', 'a'> 会被拆分为 'a' extends 'a'、'b' extends 'a'、'c' extends 'a'。用伪代码表示类似于:
function Demo(T, U) { return T.map(val => { if (val !== U) return val return 'never' }) } Demo(['a', 'b', 'c'], 'a') // ['never', 'b', 'c']
此外根据 never 类型的定义 —— never 类型可分配给每种类型,但是没有类型可以分配给 never(除了 never 本身)。即 never | 'b' | 'c' 等价于 'b' | 'c'。
因此 Demo<'a' | 'b' | 'c', 'a'> 的结果并不是 'a' | 'b' | 'c' 而是 'b' | 'c'。
心细的读者可能已经发现了 Demo 类型的声明过程其实就是 TypeScript 官方提供的工具类型中 Exclude
type T = Demo<'a' | 'b' | 'c', 'a'> // T: 'b' | 'c'
基于 Demo 类型定义,进一步地还可以实现官方工具类型中的 Omit
中满足 keys 类型的属性值。
type Omit= { [P in Demo ]: Type } interface Todo { title: string; description: string; completed: boolean; } type T = Omit
// T: { title: string; completed: boolean }
如果想让 Demo<'a' | 'b' | 'c', 'a'> 的结果为 'a' | 'b' | 'c' 是否可以实现呢? 根据官网描述:
Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.
如果不想遍历泛型中的每一个类型,可以用方括号将泛型给括起来以表示使用该泛型的整体部分。
type Demo
type Demo= [T] extends [U] ? never : T // result 此时类型为 'a' | 'b' | 'c' type result = Demo<'a' | 'b' | 'c', 'a'>
在箭头函数中使用三元表达式时,从左向右的阅读习惯导致函数内容区若不加括号则会让使用方感到困惑。比如下方代码中 x 是函数类型还是布尔类型呢?
// The intent is not clear. var x = a => 1 ? true : false
在 eslint 规则 no-confusing-arrow 中,推荐如下写法:
var x = a => (1 ? true : false)
在 TypeScript 的类型定义中,若在箭头函数中使用 extends 也是同理,由于从左向右的阅读习惯,也会导致阅读者对类型代码的执行顺序感到困惑。
type Curry= (arg: Head
) => HasTail
extends true ? Curry
, R> : R
因此在箭头函数中使用 extends 建议加上括号,对于进行 code review 有很大的帮助。
type Curry= (arg: Head
) => (HasTail
extends true ? Curry
, R> : R)
在 TypeScript 中,一般会结合 extends 来使用类型推导 infer 语法。使用它可以实现自动推导类型的目的。比如用其来实现工具类型 ReturnType
type ReturnType
type ReturnType= T extends (...args: any) => infer U ? U : never MyReturnType<() => string> // string MyReturnType<() => Promise // Promise
结合 extends 与类型推导还可以实现与数组相关的 Pop
Pop
type Pop= T extends [...infer ExceptLast, any] ? ExceptLast : never type T = Pop<[3, 2, 1]> // T: [3, 2]
Shift
type Shift= T extends [infer _, ...infer O] ? O : never type T = Shift<[3, 2, 1]> // T: [2, 1]
Reverse
type Reverse= T extends [infer F, ...infer Others] ? [...Reverse , F] : [] type T = Reverse<['a', 'b']> // T: ['b', 'a']
我们也可以使用条件类型来判断 A、B 两个类型是否完全相等。当前社区上主要有两种方案:
方案一: 参考 issue。
export type Equal1= [T] extends [S] ? ( [S] extends [T] ? true : false ) : false
目前该方案的唯一缺点是会将 any 类型与其它任何类型判为相等。
type T = Equal1<{x:any}, {x:number}> // T: true
方案二: 参考 issue。
export type Equal2= ( () => T extends X ? 1 : 2) extends (() => U extends Y ? 1 : 2) ? true : false
目前该方案的唯一缺点是在对交叉类型的处理上有一点瑕疵。
type T = Equal2<{x:1} & {y:2}, {x:1, y:2}> // false
感谢各位的阅读,以上就是“TypeScript条件类型怎么使用”的内容了,经过本文的学习后,相信大家对TypeScript条件类型怎么使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!