TS 类型表达中经常使用的关键字

TS 的类型系统为咱们的代码提供健壮性,可维护性的保障。javascript

固然除去一些基本类型外,咱们有时也须要表达一些复杂的类型,那这个时候灵活使用一些关键字就很方便了。html

这篇文章,就拿出一些能够方便咱们进行类型表达的关键字来看一看。java

extends

extends 在类型表达时,有下面两种用法:git

  1. 用于类型的继承
interface Person {
    name: string;
    age: number;
}

interface Player extends Person {
    item: 'ball' | 'swing';
}
复制代码
  1. 判断是不是能赋值给另外一个类型
// 若是 T 能够知足类型 Person 则返回 Person 类型,不然为 T 类型
type IsPerson<T> = T extends Person ? Person : T;
复制代码

typeof

在 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

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

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中,stringnumber 是惟二两个能够作 索引签名 的类型是一致的

interface PersonArray {
    [index: number]: Person;
}

interface PlainObject {
    [key: string]: any;
}
复制代码

infer

关于 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

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>;
}
复制代码
相关文章
相关标签/搜索