Typescript进阶和总结

Typescrip进阶和总结

前言

本文围绕Typescript的类型层级,type 与 interface、抽象类的区别,infer关键字,
Typescript中内置的一些常用工具类型,Typescript中的逆变和协变,还有其他的一些概念进行补充。

Typescript的类型层级

在Typescript中区分为原始类型,装箱类型和字面量类型。

装箱类型是javascript中提供的String,Number,Boolean,null,undefined,Bignit,Symbol,Object等数据类型。
原始类型是Typescript提供的string,number,boolean,null,undefined,bignit,symbol,object等数据类型,也可以称为拆箱类型。
除了null和undefined之外其他的在javascript中是未定义的。
字面量类型是比如说你给一个变量赋值为’aaa’或者123之类的值那么在Typescript中已经被类型推断为一个字符串类型或者数字类型。

// 如下代码, 'aaaa'字符串字面量类型继承了string原始类型,那么就可以认为string原始类型层级大于'aaaa'字符串字面量类型
type result1 = 'aaaa' extends string ? 1 : 2 // 1
type result2 = string extends 'aaaa' ? 1 : 2 // 2
type result3 = 'aaaaa' extends String ? 1 : 2 // 1
type result4 = String extends 'aaaa' ? 1 : 2 // 2
type result5 = string extends String ? 1 : 2 // 1
type result6 = String extends string ? 1 : 2 // 2
原始类型,装箱类型和字面量类型三者的层级是: 字面量类型 < 对应的原始类型 < 对应的装箱类型 < Object类型

在javascript中,一切皆对象,那么Object就是顶级,一切类型来自Object,但是Object的原型对象是null。
在javascript中创建对象有三种方法: 字面量{},new Object(),Object.create({}),
在Typescript中可以通过let obj : object = {} 来声明一个对象,此处的object的o是小写。
object和Object、{}的区别是:object代表着所有非原始类型的类型,即数组、对象与函数类型,
Object 类型是一个内置的接口,它描述了所有对象都具有的一些属性和方法,
{} 类型是一个空对象类型,它表示没有任何属性的对象(object的字面量类型)。
Typescript是javascript的超集,在javascript原有基础类型上扩展了any,never,unknown,enum等数据类型,但顶级对象依然是object。

代码演示
type result7 = {} extends object ? 1 : 2; // 1
type result8 = object extends {} ? 1 : 2; // 1
type result9 = {} extends Object ? 1 : 2; // 1
type result10 = Object extends {} ? 1 : 2; // 1
type result11 = object extends Object ? 1 : 2; // 1
type result12 = Object extends object ? 1 : 2; // 1

上面的 {} extends object |Object和object |Object extends {} 实际上是两种完全不同的比较方式。{} extends object 和 {} extends Object 意味着, {} 是 object 和 Object 的字面量类型,是从类型信息的层面出发的,即字面量类型在基础类型之上提供了更详细的类型信息。object extends {} 和 Object extends {} 则是从结构化类型系统的比较出发的,即 {} 作为一个一无所有的空对象,几乎可以被视作是所有类型的基类,万物的起源,而 object extends Object 和 Object extends object 这两者的情况就要特殊一些,它们是因为“系统设定”的问题,Object 包含了所有除 Top Type 以外的类型(基础类型、函数类型等),object 包含了所有非原始类型的类型,即数组、对象与函数类型,这就导致了你中有我、我中有你的神奇现象。
在这里,我们暂时只关注从类型信息层面出发的部分,即结论为:原始类型 < 原始类型对应的装箱类型 < Object 类型。

在下面的代码中,string、String、'aaaa’是{},Object的子类型,却不是object的子类型。
// {} 作为一个一无所有的空对象,几乎可以被视作是所有类型的基类,万物的起源
type result13 = string extends object ? 1 : 2 // 2
type result14 = string extends Object ? 1 : 2 // 1
type result15 = 'aaaa' extends object ? 1 : 2 // 2
type result16 = 'aaaa' extends Object ? 1 : 2 // 1
type result17 = String extends object ? 1 : 2 // 1
type result18 = String extends Object ? 1 : 2 // 1
type result19 = string extends {} ? 1 : 2 // 1
type result20 = string extends {} ? 1 : 2 // 1
type result21 = 'aaaa' extends {} ? 1 : 2 // 1
type result22 = 'aaaa' extends {} ? 1 : 2 // 1
type result23 = String extends {} ? 1 : 2 // 1
type result24 = String extends {} ? 1 : 2 // 1
在Typescript中,{},object,Object是互相兼容的,但是声明类型只能用{}或者object,继承或实现只能用Object。
class CustonObj1 implements Object {
   
}
class CustonObj2 extends Object {
   constructor() {
     super()
   }
}
let obj1: {} = {}
let obj2:object = {}
顶级类型 any unknown any表示任意一种数据类型,unknown 表示未知的数据类型。
type result26 = {} extends any ? 1 : 2 // 1
type result27 = any extends {} ? 1 : 2 // 1 | 2
type result28 = {} extends unknown ? 1 : 2 // 1
type result29 = unknown extends {} ? 1 : 2 // 2
type result30 = any extends unknown ? 1 : 2 // 1
type result31 = unknown extends any ? 1 : 2 // 1

类型链的最底层 never never是字面量的子类型
type result32 = 'bbbbb' extends never ? 1 : 2 // 2
type result33 = never extends 'bbbbb' ? 1 : 2 // 1
type result34 = null extends never ? 1 : 2 // 2
type result35 = undefined extends never ? 1 : 2 // 2
type result36 = never extends null ? 1 : 2 // 1
type result37 = never extends  undefined ? 1 : 2 // 1

Typescript中的type 与 interface、抽象类的区别

type 在Typescript作为类型别名存在,就是可以给一个类型重新起个名字。
// 定义基本数据类型
type n = number;
type str = string;
type bool = boolean;
type ns = null;
type u = undefined;
type an = any;
type ne = never;
type un = unknown;
// 使用
type C = n // type C = number
// 定义一个对象
type obj3 = {
  name: string
  age: string
  getName: (arg: string) => string
}
// 定义一个函数
type Func1 = {
  (name:string,age:number):any
}
// 定义一个类
type Car = {
   count : number,
   new (count:number) : void
}
// type和泛型联合使用
type Func2<T,U>  = {
  name : T,
  age : U,
  [propName:string]:unknown
}
// type和联合类型结合使用
type D = '1' | 1 | true | null | undefined
// implements
type Func3<T, U> = {
  name: T
  age: U
}
class Functionals1 implements Func3<string, number> {
  name: string = 'aaaa'
  age: number = 18
}
// type  类型别名不能继承类
interface 在Typescript中是接口,是对结构类型的契约。
// interface不能定义基本数据类型
  // 定义一个对象
interface obj4 {
  name: string
  age: number
  [propName: string]: any
}
// 定义一个函数
interface Func4 {
  size:number,
  (x:number,y:number):number
}
// 定义一个类
interface Person {
  name:string,
  new (name:string):void,
}
// 类实现接口  implements 
class person implements  Person {
   name:string = '';
   constructor(name:string) {
   }
}
class Car{
}
// 接口继承类  extends
interface CarType extends Car {
   run():void;
}
抽象类 abstract 抽象类只能被继承不能被实例化

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract关键字并且可以包含访问修饰符。

abstract class Department {
    constructor(public name: string) {
    }
    printName(): void {
        console.log('Department name: ' + this.name);
    }
    abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {
    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
    }
    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }
    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}
let department: Department; // 允许创建一个对抽象类型的引用
department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误: 方法在声明的抽象类中不存在

Typescript中的infer关键字 TypeScript 中支持通过 infer 关键字来在条件类型中提取类型的某一部分信息。

type Func = (...args:any[]) => any[]
type functional<T extends Func> = T extends (...args:any[]) => infer R ? R : never
type functionals =  functional<Func> // any[]
infer 其实就是一个占位符。
type Swap<T extends any[]> = T extends [infer A, infer B] ? [B,A] : T;
type SwapResult1 = Swap<['1',2]>  // [2.'1']
type SwapResult2 = Swap<['1',2,true]> //  ['1',2,true]
type ArrayItemType<T> = T extends Array<infer ElementType> ? ElementType : T
type Result1 = ArrayItemType<['aaa', 1, { name: 'bbbb' }, true, null, undefined]> // true | 1 | {name: 'bbbb';} | "aaa" | null | undefined
type Result2 = ArrayItemType<string> // string
type FunctionType = (...args: any[]) => any
type FunctionParamster<T extends FunctionType> = T extends (arg: infer P, ...args: any) => any ? P : never
type FunctionParamster1 = FunctionParamster<(arg: number) => number> // type FunctionParamster1 = number
type FunctionParamster2 = FunctionParamster<(...args: any[]) => any> // type FunctionParamster2 = any
type FunctionParamster3 = FunctionParamster<(args: string) => string> // type FunctionParamster3 = string
type FunctionParamster4 = FunctionParamster<() => void> // type FunctionParamster4 = unknown

Typescript的内置工具类型

type Person = {
  name : string,
  age:number,
  sex?: string
}
type User = Partial<Person>  // 将所有属性变为可选属性
// type User = {
//   name?: string | undefined;
//   age?: number | undefined;
//   sex?: string | undefined;
// }
type User = Required<Person>  // 将所有属性变为必传属性
// type User = {
//   name: string;
//   age: number;
//   sex: string;
// }
type ReadonlyUser = Readonly<Person> // 将所有属性变为只读属性
// type ReadonlyUser = {
//   readonly name: string;
//   readonly age: number;
//   readonly sex?: string | undefined;
// }
type Name = Pick<Person,'name'> // 获取接口中的某个属性
// type Name = {
//   name: string;
// }
type NotName = Omit<Person,'name'> // 去掉接口中的某个属性
// type NotName = {
//   age: number;
//   sex?: string | undefined;
// }
type Data = Record<string,string>  // 定义一个string类型的索引签名,返回值是string类型的接口
// type Data = {
//   [x: string]: string;
// }
type AExtractB = Extract<string|number,undefined|string | number> //类型交集
// type AExtractB = string | number
type A = 1 | 2 | true
type B = null | undefined | true 
type AExcludeB = Exclude<A,B>   //类型差集
// type AExcludeB = 1 | 2 以第一个传入的接口类型为准,找A里面不含有B的

Typescript中的逆变和协变

class Animal {
  asPset():void {

  }
}
class Dog extends Animal {
  dark():void {

  }
}
class Corgi extends Animal {
  cute():void {

  }
}
// 参数类型允许为Dog的父类型,不能为Dog的子类型。
// 返回值允许为Dog的子类型,不能为Dog的父类型
type DogFactory = (args:Animal)=> Dog
function transformDogAndCorgi(dogFactory:DogFactory) {
  const dog = dogFactory(new Dog())
  dog.dark()
}
function markDogDark(dog:Dog) {
  dog.dark()
}
函数类型的参数类型使用子类型逆变的方式确定是否成立,返回值使用子类型协变的方式确定是否成立。

对象的索引类型查询和访问

interface Foo1  {
  name: 'aaa'
  age: 18
}
type FooKeys = keyof Foo1 & {} // type FooKeys = "name" | "age"
type KeyTypes = Foo1[FooKeys]  //type KeyTypes = "aaa" | 18

类型查询操作符 typeof

const func = (input: string) => {
  return input.length > 10;
}

const func2: typeof func = (name: string) => {
  return name === 'jian'
}
const func = (input: string) => {
  return input.length > 10;
}

// boolean
type FuncReturnType = ReturnType<typeof func>;

结尾

 关于本文有讲的不到位的或者错误的地方请指正,谢谢大家。后续有空会继续更新Typescript相关知识。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值