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`。除了在对象文字的上下文类型中被识别之外,该接口的作用类似于任何空接口。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容