这是我参与更文挑战的第十六天,活动详情查看:更文挑战
条件类型是什么
一种由条件表达式所决定的类型, 表现形式为 T extends U ? X : Y
, 即如果类型 T
可以被赋值给类型 U
,那么结果类型就是 X
类型,否则为 Y
类型。
条件类型使类型具有了不唯一性,增加了语言的灵活性,
多个条件类型的嵌套
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
以上代码是多个条件类型的嵌套,会一次判断 T
的类型,然后返回不同的字符串。
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
// 类型推断为一个字面量类型: type T1 = "string"
type T1 = TypeName<string>;
// 类型推断为 type T2 = "object"
type T2 = TypeName<string[]>;
分布式条件类型
T extends U ? X : Y
如果类型 T
是一个联合类型, 如: (A | B) extends U ? X : Y
这个时候的结果类型,会变成多个条件类型的联合类型,
可以拆解为 (A extends U ? X :Y)| ``(B extends U ? X :Y)
TypeName
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
// 类型推断为string和object的字面量联合类型 --- type T3 = "string" | "object"
type T3 = TypeName<string | string[]>;
Diff<T,U> 等价于 Exclude<T,U>
利用此特性可以帮助我们实现一些类型的过滤。
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
type Diff<T, U> = T extends U ? never : T;
/*
类型推断为 type T4 = "b" | "c"
*/
type T4 = Diff<"a" | "b" | "c", "a" | "e">;
首先会被拆解为多个条件类型的联合类型
Diff<'a','a' | 'e'> | Diff<'b' ,'a' | 'e'> | Diff<'c' ,'a'|'e'>
a
是否可以被赋值给字面量联合类型, -可以,则返回never
b
是否可以被赋值给字面量联合类型, -不可以,则返回b
c
是否可以被赋值给字面量联合类型, -不可以,则返回b
结果为: never | b | c
最后never
是 b
,c
的联合类型,因此返回 b| c
可以从类型T
中过滤掉,可以赋值给类型U
的类型,
我们可以基于Diff
再做扩展,从类型中除去一些不需要的类型,如 undefined
,null
NotNull 等价于 NoneNullable<T,U>
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
type Diff<T, U> = T extends U ? never : T;
type NotNull<T> = Diff<T, undefined | null>;
/*
类型推断为 type T5 = string | number
*/
type T5 = NotNull<string | number | undefined | null>;
实际上,上面两个类型,官方已经有实现了,
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
// 自定义类型
type Diff<T, U> = T extends U ? never : T;
// 自定义类型
type NotNull<T> = Diff<T, undefined | null>;
/*
从类型T中过滤掉可以赋值给类型U的类型
Exclude<T,U> 等同于 Diff<T, U>
NoneNullable<T,U> 等同于 NotNull<T>
从类型T中抽取出可以赋值给类型U的类型
Extract<T,U> 与 Exclude<T,U> 相反
*/
// 类型推断为 type T6 = "a"
type T6 = Extract<"a" | "b" | "c", "a" | "e">;
ReturnType<()=>{}>
// 可以获取一个函数返回值的类型
// 类型推断为 type T7 = string
type T7 = ReturnType<() => string>;
源码及实现原理
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;
参数 T
可以被赋值给一个函数,此函数有任意的参数,返回值类型也是任意的,
由于函数的返回值类型是不确定的,所以使用了一个 infer
关键字,表示延迟推断,需要根据实际的情况来确定,
如果实际的情况是返回 类型 R
,那么结果类型就是 R
,否则返回值类型就是any
ReturnType
接受一个函数类型,并返回这个函数的返回值类型。这里关键在于 (infer R
),返回值类型 R
此时是不能确定的,只有在函
数执行之后才知道,是一种延迟推断,所以用 infer 修饰。特殊情况,比如 type T1 = ReturnType<any>
,T1
的类型是 any
。
type
和 interface
多数情况下有相同的功能,就是定义类型。但有一些小区别:
type
:不是创建新的类型,只是为一个给定的类型起一个名字。type
还可以进行联合、交叉等操作,引用起来更简洁。
interface
:创建新的类型,接口之间还可以继承、声明合并。