TS 的类型系统为咱们的代码提供健壮性,可维护性的保障。javascript
固然除去一些基本类型外,咱们有时也须要表达一些复杂的类型,那这个时候灵活使用一些关键字
就很方便了。html
这篇文章,就拿出一些能够方便咱们进行类型表达的关键字来看一看。java
extends
在类型表达时,有下面两种用法:git
interface Person { name: string; age: number; } interface Player extends Person { item: 'ball' | 'swing'; } 复制代码
// 若是 T 能够知足类型 Person 则返回 Person 类型,不然为 T 类型 type IsPerson<T> = T extends Person ? Person : T; 复制代码
在 TS 中用于类型表达时,typeof
能够用于从一个变量上获取它的类型。github
举一个没有卵用的例子:typescript
const value: number = 10; const value2: typeof vlaue = 100; // const value2: number 复制代码
可是请注意下面这种状况:数组
const value = 10; const value2: typeof vlaue = 100; // const value2: 10 // ERROR: Type '100' is not assignable to type '10' 复制代码
经测试,number
string
boolean
类型在没有声明而直接赋值的状况下,都会存在这个问题markdown
对于对象,数组,函数类型来说,这个仍是有点用的。参考函数
const data = { value: 123, text: 'text', subData: { value: false } }; type Data = typeof data; // type Data = { // value: number; // text: string; // subData: { // value: boolean; // }; // } 复制代码
keyof
是TS中的索引类型查询操做符。keyof T
会获得由 T
上已知的公共属性名组成的联合类型。oop
Keyof T, the index type query operator. For any type T, keyof T is the union of known, public property names of T
举个例子:
interface Person { name: string; age: number; phoneNum: number; } type PersonProperty = keyof Person; // type PersonProperty = "name" | "age" | "phoneNum" 复制代码
keyof
在咱们限制类型或者枚举属性时仍是很是常见的,好比下面这个小例子:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
复制代码
这样当咱们尝试获取不在目标对象上的属性值时,TS会为咱们检查到这样简单的错误
T[K]
在TS里称做索引访问操做符(indexed access operator
)。它能够为咱们准确解析目标对象上的对应属性的正确类型。
在下面的介绍,咱们能够继续看到 keyof
的应用。
in
操做符用于遍历目标类型的公开属性名。相似 for .. in
的机制。
从其用途看,咱们很容易想到 in
可用于联合类型或者枚举类型。
咱们能够像下面这样使用枚举类型:
enum Letter { A, B, C, } type LetterMap = { [key in Letter]: string; } // type LetterMap = { // 0: string; // 1: string; // 2: string; // } 复制代码
咱们能够像下面这样使用联合类型:
type Property = 'name' | 'age' | 'phoneNum'; type PropertyObject = { [key in Property]: string; } // type PropertyObject = { // name: string; // age: string; // phoneNum: string; // } 复制代码
利用可用于联合类型的特性,咱们有下面这种很常见的作法(仅举例):
type ToString<T> { [key in keyof T]: string; } 复制代码
值得一提的是,一些基础类型(string
, number
, symbol
)也能够用于 in
操做符:
type StringKey = { [key in string]: any; } // type StringKey = { // [x: string]: any; // } type NumberKey = { [key in number]: any; } // type NumberKey = { // [x: number]: any; // } type SymbolKey = { [key in symbol]: any; } // 这里和预想的不同 TODO // type SymbolKey = {} 复制代码
关于这一点,我认为和TS中,string
与 number
是惟二两个能够作 索引签名 的类型是一致的
interface PersonArray {
[index: number]: Person;
}
interface PlainObject {
[key: string]: any;
}
复制代码
关于 infer
操做符,这个能够用来进行类型推测。举个简单的小例子:
在 TS 中,若是咱们在 generator
函数中使用了 yield
表达式,咱们就会丢失类型。好比下面这样:
function returnSomething() { // return something; } function* task() { // 这里的 result 在TS中是没有拿到正确的函数返回类型的,你们能够试一下 const result = yield returnSomething(); } 复制代码
那为了解决相似的问题,TS 为咱们内置了 ReturnType
的映射类型:
function* task() { // 这里的 result 在TS中是没有拿到正确的函数返回类型的,你们能够试一下 const result: ReturnType<typeof returnSomething> = yield returnSomething(); } 复制代码
那咱们来看一下 ReturnType
是如何作的呢,其实很简单,就是用到了 infer
:
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
复制代码
infer P
中的 P
便是表示待推断的返回值类型。
关于 infer
的更多内容,你们能够参考这篇文章,也但愿你们能够多多支持做者
is
操做符用于TS的类型谓词中,是实现TS类型保护的一种方式(关于什么是类型保护)。
好比下面这种场景:
function doSometing(value: string | number) { if (typeof value === 'string') { // TS 能够识别这个分支中 value 是 string 类型的参数(这就叫类型保护) // do something } else { // TS 能够识别这个分支中 value 是 number 类型的参数 // do something } } 复制代码
除去上面这种方式之外,咱们可使用TS的类型谓词来实现:
/** * 此函数用于判断参数 value 是否是 string 类型 * * 因为返回类型声明了类型谓词,能够帮助TS在代码分支中进行类型保护(默认返回 boolean 类型是没办法作到的) **/ function isString(value: any): value is string { return typeof value === 'string'; } function doSometing(value: string | number) { if (isString(value)) { // TS 能够识别这个分支中 value 是 string 类型的参数(这就叫类型保护) } else { // TS 能够识别这个分支中 value 是 number 类型的参数 } } 复制代码
这样作的好处是:实现了代码复用,实现了更好的语义化。
其实,TS 代码中 Array.isArray
即是使用了这样的声明。
interface ArrayConstructor { // ... isArray(arg: any): arg is Array<any>; } 复制代码