TS-高级类型

  • 类型保护(Type Guards)
  • 可空类型(Nullable types)
  • 类型别名(Type Aliases)
  • 多态this
  • 索引类型
  • 映射类型
  • 条件类型
  • TS库中的内置类型转换实用类型

1.类型保护(Type Guards)

类型保护就是确保该类型在一定的范围内可以运行,简单来说就是当一个值为number类型或者string类型,类型保护就是当它为number类型时确保它的值为number,当它为string类型时确保它的值为sting
实现类型保护常用的有几下几种方法

  • in
  • instanceof
  • typeof
  • 自定义类型
class Bird {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  fly() {
    console.log("fly")
  }
}
class Fish {
  name: string;
  constructor(name: string){
    this.name = name;
  }
  swim(){
    console.log("swim")
  }
}
declare function getSmallPet(): Fish | Bird;
let pet = getSmallPet();
pet.name;
pet.swim(); //error 类型“Fish | Bird”上不存在属性“swim”

如上,假如当前pet确实是Fish类型的,我们想用到swim方法,我们就可以使用类型保护来确保typescript识别他的具体类型;
注: 当然我们可以使用类型断言 as,但是这不是我们想要的

使用 in
 if("swim" in pet){
  pet.swim();  //ok
}
使用 instanceof
if(pet instanceof Fish){
  pet.swim() //ok
}
使用 typeof

注: typeof 只能判断该类型的基本类型,如"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"

function getSum(arg: number | string): number{
  if(typeof arg === "number"){
    return arg++
  }else{
    return parseInt(arg) + 1
  }
}
自定义的类型保护
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}
if(isFish(pet)){
  pet.swim();
}

2.可空类型(Nullable types)

TypeScript有两个特殊类型,null和undefined

let num: number | undefined = 123;
let obj: object | null = {};

// 此时求和就可以使用类型保护来避免出现错误
function getSum(){
  if(typeof num === 'number'){
    num++
  }
}

3. 类型别名(Type Aliases)

类型别名即为类型创建新名称。比如为我们的接口数据定义一个类型Result:

type Result<T> ={
  code: number;
  data: T,
  message: string
}

此时的Result即为类型别名

4.多态this

多态this类型是某个包含的类或接口的子类型;
this类型它能表现连贯接口间的继承简易性

class Base {
  constructor(
    public value: number 
  ){}

  add(num: number): this{
    this.value += num;
    return this;
  }

  multiply(num: number): this {
    this.value *= num;
    return this;
  }
}

let a = new BaseCalculator(3);
a.add(7).multiply(5).add(5).value;//链式

使用this类型,可以对其进行扩展,并且新类可以使用旧类方法

//类可以拓展
class Child extends Base {
  constructor(value: number = 0){
    super(value);
  }
  public sin() {
    this.value = Math.sin(this.value);
    return this;
  }
}
let b = new ScientificCalculator();
b.sin().add(1).multiply(4).value;

5.索引类型

索引类型即可被索引的类型,对应JavaScript 中的数组、对象等聚合多个元素的类型。
keyof 为常用的索引查询运算符
比如: 我们想获取某个对象中某个属性值

function getProperty<T, K extends keyof T>(o: T, propertyName: K): T[K] {
  return o[propertyName]; 
}

interface People {
  name: string;
  age: number;
  say(): void;
}
let li: People = {
  name: "li",
  age: 18,
  say(){
    console.log("i am li")
  }
}
const  liName = getProperty(li,"name");

6.映射类型

即类型可以映射,常用于基于旧类型创建新类型;
TS库中已内置映射方法有Partial,Readonly, Pick, Record
注:
Partial,Readonly, Pick为同态,即不会创建新属性 添加新成员只能用&
Record为非同态,即会创建新属性, 新属性由Record第一个属性指定

//将某类型全部变为可选属性
type Partial<T> = {
  [P in keyof T]?: T[P];
};
//将某类型变为readonly类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};
//抽取目标类型的子集类型
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};
//非同态: 会创建新属性, 新属性由Record第一个属性指定
type Record<K extends keyof any, T> = {
  [P in K]: T;
};
//eg:
interface People{
  name: string,
  age: number,
  sex: string
}
type PartialMan = Partial<People>;//Partial
type ReadonlyMan = Readonly<People>;//Readonly
type PickMan = Pick<People, "name" | "age">;//Pick

let PartialLilei: PartialMan = {
  name: "lilei",
  age: 18
}
let PickLilei: PickMan = {
  name: 'lilei',
  age: 18
}

//同态添加新成员只能用&
type Woman=  Readonly<People> & { say(): void};

//Record
enum method {
  Get= 'get',
  // Post= 'post',
  // Delete= 'delete'
};
interface HttpFn {
  (url: string, config?: any) : Promise<void>
}
type RecordHttp = Record<method, HttpFn>;

let httpget: RecordHttp = {
  get: (url: string, config?: any) => {
      //return new Promise((reslove,reject) => {
      //reslove()
      //})
  },
  post:(url: string, config?: any) => {
      //return new Promise((reslove,reject) => {
      //reslove()
      //})
  }
}

7 条件类型

根据条件判断来推测其类型。常用表达式为:

T extends U ? X : Y;

上面表达式含义为:若 T 能够分配(赋值)给 U(即是U的子类型),那么类型是 X,否则为 Y

type value<T> = T extends boolean ? string : number;
let val:  value<false> = "123";//ok
let val: value<true> = 123; //error

条件延时推测---即当推测条件不足时,会推迟条件判断,直至条件充足时;

interface Foo {
    propA: boolean;
    propB: boolean;
}

declare function f<T>(x: T): T extends Foo ? string : number;

function foo<U>(x: U) {
     // 因为 ”x“ 未知,因此判断条件不足,不能确定条件分支,推迟条件判断直到 ”x“ 明确,
    // 推迟过程中,”a“ 的类型为分支条件类型组成的联合类型,string | number 联合类型
    let a = f(x);
    // 这么做是完全可以的
    let b: string | number = a;
}
分布条件类型

即:其中选中的类型为裸类型参数的条件类型称为分布式条件类型;例化期间,分布条件类型自动分布在联合类型上。例如,T extends U ? X : Y使用类型参数A | B | Cfor的实例化T解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
注: 裸类型为 类型参数没有被包装在其他类型里,比如没有被数组、元组、函数、Promise,对象等包裹
如:

// 裸类型,没有被任何其他类型包裹,即T
type NakedType<T> = T extends boolean ? "YES" : "NO"
// 非裸类型,类型参数被包裹的在元组内,即[T]
type WrappedType<T> = [T] extends [boolean] ? "YES" : "NO";

条件类型的分布属性可以方便地用于过滤联合类型:

//过滤联合类型中的null和undefined,
type NonNullable<T> = T extends null | undefined ? never : T
//never 类型表示不会是任何值,即什么都没有

type a = string | number| Function | null | undefined;
let b: NotNullable<a>  = 1;//ok
b = null; //error
条件类型与映射类型

条件类型和映射类型配合使用特别有用

//提取类型中属性值为Funtion类型key值,相当于 {key: key | never}[key]
type FunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

//FunctionPropertyNames<T>为key值,Pick是提取属性值为FunctionPropertyNames<T>的值,相当于{key: T[key]}
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

//提取类型中属性值不为Funtion类型key值,相当于 {key: key | never}[key]
type NonFunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];

type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

interface Part {
  id: number;
  name: string;
  subparts: Part[];
  updatePart(newName: string): void;
}

type T1 = FunctionPropertyNames<Part>;
//type T1 = "updatePart"

type T2 = FunctionProperties<Part>;
// type T3 = {
//       updatePart: (newName: string) => void;
//   }

type T3 = NonFunctionPropertyNames<Part>;
// type T2 = "id" | "name" | "subparts"

type T4 = NonFunctionProperties<Part>;
// type T4 = {
//       id: number;
//       name: string;
//       subparts: Part[];
//   }

8 TS库中的内置类型转换实用类型

1. Partial<Type>  //将某类型全部变为可选属性,前面已介绍
2. Readonly<Type> //将某类型全部变为Readonly属性,前面已介绍
3. Pick<Type, Keys> //抽取目标类型的子集类型,前面已介绍
3. Record<Keys,Type> //非同态: 会创建新属性, 新属性由Record第一个属性指定 ,前面已介绍
4. Omit<Type, Keys> //移除某一类型中的属性
interface People{
  name: string,
  age: number,
  sex: string,
  height: number
}
type Man = Omit<People, 'height'>
/*
 { name: string,
  age: number,
  sex: string
}
*/
5. Exclude<Type, ExcludedUnion>//去除联合类型中指定类型
type T0 = Exclude<"a" | "b" | "c" | "d","a" | "b">;
// type T0 = "c" | "d";
6. Extract<Type, Union>//返回联合类型中相同类型
type T0 = Exclude<"a" | "b" | "c" | "d","a" | "f">;
// type T0 = "a";
7. NonNullable<Type>//去除类型中null和undefined;
type T1 = NonNullable<string[] | string | null | undefined>;
// type T1 = string[] | string;
8. Parameters<Type>//提取函数类型中参数类型,返回参数类型元祖,非函数返回unkown
type T0 = Parameters<() => string>;
// type T0 = []
type T1 = Parameters<(s: string) => void>;
// type T1 = [s: string]
type T2 = Parameters<any>;
// type T2 = unknown[]
type T3 = Parameters<never>;
// type T2 = never
9. ConstructorParameters<Type>//返回构造函数中Constructor参数元组
type T0 = ConstructorParameters<ErrorConstructor>;
// type T0 = [message?: string]
type T1 = ConstructorParameters<FunctionConstructor>;
// type T1 = string[]
type T2 = ConstructorParameters<RegExpConstructor>;
// type T2 = [pattern: string | RegExp, flags?: string]
type T3 = ConstructorParameters<any>;
// type T3 = unknown[]
10. ReturnType<Type>//返回函数的返回类型组成的类型。
type T0 = ReturnType<() => string>;
//type T0 = string
type T1 = ReturnType<(s: string) => void>;
//type T1 = void
type T2 = ReturnType<<T>() => T>;
// type T2 = unknown

11. InstanceType<Type>//返回构造函数的实例类型组成的类型
class C {
  x = 0;
  y = 0;
}
type T0 = InstanceType<typeof C>;
//type T0 = C
type T1 = InstanceType<any>;
// type T1 = any
type T2 = InstanceType<never>;
//type T2 = never

12. Required<Type>//与Partial相反,将类型中可选参数变为必选
interface Props {
  a?: number;
  b?: string;
}
const obj: Required<Props>;
//obj: {
// a: number;
 // b: string;
//}

13.ThisParameterType<Type>//获取函数中 this的数据类型,如果没有则返回 unknown 类型
function toHex(this: Number) {
  return this.toString(16);
}

function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}

14.OmitThisParameter<Type>//移除函数中的 this 数据类型:
function toHex(this: Number) {
  return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
console.log(fiveToHex());

15.ThisType<Type>//该实用程序不返回转换后的类型。相反,它用作上下文this类型的标记。请注意,--noImplicitThis必须启用该标志才能使用此实用程序
type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}
let obj = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx; // Strongly typed this
      this.y += dy; // Strongly typed this
    },
  },
});
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
//在上面的例子中,`methods`在参数对象`makeObject`具有上下文类型,其包括`ThisType<D & M>`和因此的类型[此](https://www.typescriptlang.org/docs/handbook/functions.html#this)内的方法`methods`目的是`{ x: number, y: number } & { moveBy(dx: number, dy: number): number }`。请注意,`methods`属性的类型如何同时成为`this`方法中类型的推断目标和来源。

//该`ThisType<T>`标记接口是简单地宣布一个空的接口`lib.d.ts`。除了在对象文字的上下文类型中被识别之外,该接口的作用类似于任何空接口。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容