likes
comments
collection
share

TS操作符,学好TS的第一步

作者站长头像
站长
· 阅读数 55

序言

最近开始学习TS,发现了很多困难,过了这道坎以后,发现TS的操作符就像拼音是汉字的基础一样,学好TS操作符,是学好TS的重中之重,我们今天就来详细的了解一下TS的操作符。

typeof

在 TypeScript中,typeof 操作符可以用来获取一个变量的声明,或是对象的类型。我们看两个小例子

//例子1

interface People {
  name: string;
  age: number;
}

const variableDai: People = { name: 'coolFish', age: 24 };
type formDai= typeof variableDai; // -> People

//例子2

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]

小结:要注意的是,例子1中我们定义了一个常量 variableDai 然后获取他的类型,赋值给了类型 formDai。

Keyof

keyof 操作符可以用来一个对象中的所有 key 值, 返回的是这些key值得联合类型。

interface Person {
    name: string;
    age: number;
}

type allKey = keyof Person; // "name" | "age"

当然我们可以利用这个特性来做一些特殊的操作,例如遍历一个空数组,返回一个数组所拥有的所有属性的联合类型

interface Person {
    name: string;
    age: number;
}

type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"

例子3 ​

type K3 = keyof { [x: string]: Person }; // string | number

因为这里的键是索引类型,所以是 string | number

in

in 用来遍历枚举类型

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} 
//{ a: any, b: any, c: any }

infer

在条件类型语句中,可以用infer 声明一个类型变量,并且对它进行使用。

//返回数组的第一项
type Head<T extends Array<any>> =  T extends [head : infer H, ...rest : any[]] ? H : never;

// 测试用例
type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[3, 2]> // 3

上例的原理是,我们用infer定义一个类型变量为第一项,然后类型收窄,就获得了第一项。

extends

当我们定义的泛型不想过于灵活,或者想继承某些类的时候,我们可以通过 extends 关键字添加泛型约束

interface mustLength {
  length: number;
}

function mustTakeLength<T extends mustLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}
mustTakeLength(3)// Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3});//10

我们定义了一个 mustLength 的接口,然后 入参受该接口约束,必须有一个 length 属性。否则就会报错 ​

我们再看一个例子,来理解一下extends

type User = {
  id: number;
  kind: string;
};

function makeCustomer<T extends User>(u: T): T {
  // Error(TS 编译器版本:v4.4.2
  // Type '{ id: number; kind: string; }' is not assignable to type 'T'.
  // '{ id: number; kind: string; }' is assignable to the constraint of type 'T', 
  // but 'T' could be instantiated with a different subtype of constraint 'User'.
  return {
    id: u.id,
    kind: 'customer'
  }
}

上面的代码为什么会报错?因为 T 受 User 约束,必须拥有 User中的类型,但不局限只拥有,也能拥有更多的类型,返回只返回了两种,如果有更多类型,就会出问题,所以报错,解决方案如下


type User = {
  id: number;
  kind: string;
};

// T assignable User
function makeCustomer<T extends User>(u: T): T {
  return {
    ...u,
    id: u.id,
    kind: 'customer'
  }
}

用扩展运算符将多余参数返回即可。

Partial

Partial<T> 的作用就是将某个类型里的属性全部变成可选,该方法是联合了 keyof 和 in实现的。

type Partial<T> = {
    [P in keyof T]?: T[P];   
};
interface User {
    name:string,
  age:number,
  department:string
}
type optional = Partial<User>
/**type optional = {
    name?: string | undefined;
    age?: number | undefined;
    department?: string | undefined;
}**/

Required

Required<T> 的作用就是将某个类型中的属性全部变为必选,具体实现和Partial类似

type Required<T> = {
    [P in keyof T]-?: T[P];
};

与 Partial 不同的就是,Partial 是加上 ? 而这里是移除 ?。

Readonly

Readonly<T> 的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

在每一个key前面加上 readonly ​

Record

Record<K extends keyof any,T> 的作用是将 K 中所有的属性的值转换为 T 类型

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

小结:将K 中有类型的属性进行遍历,再将每个 Key 赋值 T类型 实例

type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
    name:string,
    age:number,
}

type IPets = Record<petsGroup, IPetInfo>;

const animalsInfo:IPets = {
    dog:{
        name:'dogName',
        age:2
    },
    cat:{
        name:'catName',
        age:3
    },
    fish:{
        name:'fishName',
        age:5
    }
}

Pick

Pick<T,K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子属性

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

T 是多类型,K 是T的部分类型,然后遍历K拿到键,再去T里拿到相应的值。

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false
};

Exclude

Exclude<T,U> 的作用是将某个类型中属于另一个的类型移除掉

type Exclude<T, U> = T extends U ? never : T;

如果 T 能赋值给 U 类型的话,那么就会返回 never 类型,否则返回 T 类型。最终实现的效果就是将 T 中某些属于 U 的类型移除掉。这里主要是利用了 never 的特性,never 是最底层类型,联合任意类型等于那个类型,已达到移除的效果。

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

Omit

Omit<T, K extends keyof any> 的作用是使用 T 类型中除了 K 类型的所有属性

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false
};

我们要实现上面的结果,可以利用 Pick 和 Exclude ,Pick可以挑选出我们想要的,Exclude可以排除掉我们不想要的,所以我们只需要先排除掉 description,再从 Todo 里面 挑选剩余的,就能完成,如下是实现的代码。

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
转载自:https://juejin.cn/post/7010338170592051207
评论
请登录