TypeScript高级类型及用法

高级类型

交叉类型

交叉类型是将多个类型合并为一个类型,可以把多种类型叠加到一起成为一种类型 它包含了所需的所有类型的特性

  • 语法:T&U
    其返回类型既要符合T类型 也要符合U类型
  • 用法: 假设有两个接口 一个是Ant蚂蚁接口 一个是Fly飞翔接口 现在有一只会飞的蚂蚁:
interface Ant{
  name:string;
  weight:number;
}

interface Fly{
  flyHeight:number;
  speed:number
}

//少了任何就一个属性都会报错
const flyAnt:Ant&Fly={
  name:'蚂蚁雅黑',
  weight:0.2,
  flyHeight:20,
  speed:1
}

联合类型

联合类型与交叉类型很有关系 但是使用上却完全不同

  • 语法: T | U
  • 其返回类型为连接的多个类型中的任意一个
  • 用法:假设声明一个数据 既可以是string类型 也可以是number类型
let stringOrNumber:string|number = 0
stringOrNumber = ""

再看下面的这个例子 start函数的参数类型既是Bird|Fish 那么在start函数中 想要直接调用的话,
只能调用Bird和Fish都具备的方法,否则编译会报错。

class Bird {
  fly(){
    console.log('bird flying');
  }
  lagEggs(){
    console.log('bird layEggs');
  }
}

class Fish{
  swim(){
    console.log("Fish swimmimg");
  }
  layEggs(){
    console.log('fish layEggs');
  }
}

const bird = new Bird()
const fish = new Fish()

function start(pet:Bird|Fish){
  //可调用layEggs, 因为Bird 和Fish都有这个方法
  pet.layEggs()

//会报错:Property 'swim' does not exist on type "Bird|Fish"
  // pet.swim()
}

start(bird)

start(fish)

二 关键字

类型约束

语法: T extends K
这里的extends 不是类、接口的继承 而是对于类型的判断和约束,意思是判断T能否赋值给K
可以在泛型中对传入的类型进行约束

const copy = (value:string|number):string|number =>value
//只能传入string或者number
copy(10)

//会报错Argumnets of type 'boolean' is not assignable to parameter of type "string|number"
copy(false)

也可以判断T是够可以赋值给U ,可以的话返回T,否则返回never

type Exclude= T extends U ? T :never

类型映射

会遍历指定接口的key 或者是遍历联合类型


interface Person{
    name:string,
    age:number,
    gender:number
}

//将T的所有属性转换为只读类型

type ReadOnlyType={
    readonly [P in keyof T]:T
}

// type ReadonlyType = {
//     readonly name: Person;
//     readonly age: Person;
//     readonly gender: Person;
// }

type ReadOnlyPerson = ReadOnlyType

类型谓词{is}

语法:parameterName is Type
parameterName 必须是来自当前函数签名里的一个参数名
判断parameterName是否是Type类型

如果想要在start函数中 根据情况去调用Bird的fly方法和fish的swim方法
首先想到的可能是直接检查成员是否存在,然后进行调用

function start(pet:Bird|Fish){
    //调用layEggs没问题  因为Bird或者Fish都有layEggs方法
    pet.layEggs();
    if((pet as Bird).fly){
        (pet as Bird).fly()
    }else if((pet as Fish).swim){
        (pet as Fish).swim()
    }
}

但是这样做,判断以及调用的时候都要进行类型转换,未免有些麻烦,可能会想到写个工具函数判断下

function isBird(bird:Bird | Fish):boolean{
    return !!(bird as Bird).fly
}

function isFish(fish:Bird|Fish):boolean{
    return !!(fish as Fish).swim
}
function start(pet:Bird|Fish){
    //调用layEggs 没问题 因为Bird或者Fish都有layEggs方法
    pet.layEggs()

    if(isBird(pet)){
        (pet as Bird).fly()
    }else if(isFish(pet)){
        (pet as Fish).swim()
    }
}

看起来简洁了一点 但是调用方法的时候 还是要进行类型转换才可以,否则还是会报错,那有什么好的方法,能让我们判断完类型之后,就可以直接调用方法,不用再进行类型转换呢?
OK,肯定是有的,类型谓词is就派上用场了

  • 用法
function isBird(bird:Bird|Fish):bird is Bird{
    return !!(bird as Bird).fly
}

function start(pet:Bird|Fish){
    pet.layEggs()
    if(isBird(pet)){
        pet.fly()
    }else{
        pet.swim()
    }
}

每当使用一些变量调用isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
TypeScript不仅知道在If分支里pet是Bird类型;它还清楚在else分支里,一定不是Bird类型,一定是Fish类型

待推断类型【infer】

可以用infer P来标记一个泛型,表示这个泛型是一个待推断的类型,并且可以直接使用
比如下面这个获取函数参数类型的例子:

type ParamType = T extends (param:infer P) =>any ? P:T;
type FunctionType = (value:number)=>boolean
type Param = ParamType; //type Param = number
type OtherParam = ParamType//type Param = symbol

判断T是否能够赋值给(Param:infer P)=>any,并且将参数推断为泛型P,如果可以赋值 则返回参数类型P, 否则返回传入的类型

原始类型保护(typeof)

语法:typeof v === “typename” 或 typeof v !== “typename”
用来判断数据的类型是否是某个原始类型(number,string,boolean,symbol)并进行类型保护
typename必须是number,string,boolean或symbol,但是TypeScript并不会阻止你与其他字符串比较,语言不会把那些表达式识别为类型保护。

看下面这个例子,print函数会根据参数类型打印不同的结果,那如何判断参数是string还是number呢

function print(value:number|string){
    //如果是string类型
    // console.log(value.split('').join(','))

//如果是number类型
//console.log(value.toFixed(2))
}

有两种常用判断方式:
1.根据是否包含split属性判断是string类型,是否包含toFixed方法判断是number类型
弊端:不论是判断还是调用都要进行类型转换
2.使用类型谓词is
弊端:每次都要去写一个工具函数 太麻烦了
用法:这就到了typeof一展身手的时候了

function print(value:number|string){
    if(typeof value ==="string"){
        console.log(value.split('').join(','));
    }else{
        console.log(value.toFixed(2));
    }
}

使用typeof进行类型判断后,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的类型是兼容的

类型保护(instanceof)

与typeof类似,不过作用方式不同 instanceof类型保护是通过构造函数来细化类型的一种方式
instanceof的右侧要求是一个构造函数 TypeScript将细化为:
此构造函数的prototype属性的类型,如果它的类型不为any的话
构造签名所返回的类型的联合
还是以类型谓词is示例中的代码做演示:
最初代码:

function start(pet:Bird|Fish){
    //调用layEggs没问题,因为Bird或者Fish都有layEggs方法
    pet.layEggs()
    if((pet as Bird).fly){
        (pet as Bird).fly()
    }else if((pet as Fish).swim){
        (pet as Fish).swim()
    }
}

使用instanceof 后的代码:

function start(pet:Bird|Fish){
    pet.layEggs()
    if(pet instanceof Bird){
        pet.fly()
    }else{
        pet.swim()
    }
}

可以达到相同的效果

索引类型查询操作符【keyof】

语法:keyof T
对于任何类型 T,keyof T 的结果为T上已知的公共属性名的联合

interface Person{
    name:string;
    age:number
}
type PersonProps = keyof Person; //name|age

这里,keyof Person 返回的类型和name|age联合类型是一样,完全可以互相替换
//用法:keyof只能返回类型上已知的公共属性名

class Animal{
    type:string;
    weight:number;
    private speed:number
}
type AnimalProps = keyof Animal;//type|weight

例如我们经常会获取对象的某个属性值,但是不确定是哪个属性,这个时候可以使用extends配合typeof 对属性名进行限制 限制传入的参数只能是对象的属性名


const person = {
    name:'Jack',
    age:20
}
function getPersonValue(fieldName: keyof typeof person){
    return person[fieldName]
}
const nameValue = getPersonValue('name')
const ageValue = getPersonValue('age')
//会报错:Argument of type "gender" is not assignable to parameter of type "name"|"age"
// getPersonValue('gender')

索引访问返回符【T[K]】

语法:T[K]
类似于js中使用对象索引的方式 只不过js中返回对象属性的值,而在ts中返回的是T对应属性P的类型
用法

interface Person{
    name:string
    age:number
    weight:number|string
    gender:'man'|'women'
}

type NameType = Person['name'] //string
type WeightType = Person['weight'] //string|number
type GenderType = Person['gender'] //'man'|'women'

三.隐射类型

###只读类型【Readonly】
定义:

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

用于将T类型的所有属性设置为只读状态
用法:

interface Person{
    name:string
    age:number
}

const person: Readonly = {
  name:'Lucy',
  age:22
}

//会报错:Cannot assign to "name" because it is a read-only property
// person.name = "Lily"

readonly 只读 被readonly标记的属性只能声明时或类的构造函数中赋值 之后将不可改(即只读属性)

只读数组【ReadonlyArray】

定义

interface ReadonlyArray{
    // Iterator of values in the array
    [Symbol.iterator]():IterableIterator;
    // Return an iterable of keyof,value pairs for every entry in the Array
    entries():IterableIterator<[number,T]>;
    // Returns an iterable of keys in the Array
    keys():IterableIterator;
    // Return an iterable of values in the Array
    values():IterableIterator
}

只能在数组初始时为变量赋值,之后数组无法修改
使用

interface Person{
    name:string
}
const personList:ReadonlyArray = [{name:"Jack"},{name:'Rose'}]
//会报错:Property "push" does not exist on type 'readonly Person[]'
// personList.push({name:'Lucy'})

//但是内部元素如果是引用类型  元素自身是可以进行修改的
personList[0].name = 'Lily'

可选类型{Partial}

用于将T类型的所有属性设置为可选状态,首先通过keyof T,取出类型T的所有属性,然后通过in操作符进行遍历,最后在属性后加上?,将属性变为可选属性
定义

type Partial = {
    [P in keyof T]?:T[P];
}

用法

interface Person{
    name:string
    age:number
}

//会报错:Type "{}" is missing the following properties from type "Person":name,age
//let person:Person = {}

//使用Partial 隐射后返回的新类型,name和age都变成了可选属性

let person:Partial={}
person = {name:"pengzu",age:800}
person = {name:'z'}
person = {age:18}

必选类型(Required)

和Partial的作用相反
用于将T类型的所有属性设置为必选属性 首先通过keyof T,取出类型T的所有属性,然后通过in操作符进行遍历,最后在属性后的?前加上-,将属性变为必选属性
定义

type Required{
    [P in keyof T]-?:T[P]
}
// 使用

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

// 使用Required 隐射后返回的新类型 name和age都变成了必选属性

//会报错:Type "{}" is missing the following properties from type "Required":name,age
let person:Required = {}

提取属性(Pick)

定义

type Pick={
    [P in K]:T[P]
}

从T类型中提取部分属性 作为新的返回类型
使用:比如我们在发送网络请求时,只需要传递类型中的部分属性,就可以通过Pick来实现

interface Goods{
    type:string
    goodsName:string
    price:number
}

//作为网络请求参数  只需要goodsName和price就可以
type RequestGoodsParams = Pick
//返回类型
//type RequestGoodsParams = {
//goodsName:string;
//price:number
// }
const params:RequestGoodsParams= {
    goodsName:'',
    price:10
}

排除属性{Omit}

定义:type Omit = Pick>
和Pick作用相反,用于从T类型中 排除部分属性

用法比如长方体有长宽高,而正方体长宽高相等,所以只需要长就可以了,那么此时就可以使用Omit来生成正方体的类型

interface Reactanglar{
    length:number;
    height:number;
    width:number;
}

type Square = Omit

// 返回类型
// type Square = {
//     length:number
// }

const temp:Square = {length:5}

摘取类型(Extract

语法:Extract
提取T中可以赋值给U的类型
定义:

type Extract = T extends U?T:never

用法:

type T01 = Extract<'a'|'b'|'c'|'d','a','c','f'> //a|c


type T02 = Extractvoid),Function>//()=>void

排除类型(Exclude

语法:Exclude
与Extract用法相反 从T中剔除可以赋值给U的类型

定义:type Exclude = T extends U? never:T

用法:

type T00 = Exclude<'a'|'b'|'c'|'d','a'|'c'|'f'>//'b'|'d'
type T01 = Excludevoid),Function> //string|number

属性映射{Record}

定义:

type Record={
    [P in K]:T
}

接收两个泛型,K必须可以是可以赋值给string|number|symbol的类型,通过in 操作符对K进行遍历,每一个属性的类型都必须是T类型

**用法:比如我们想要将Person类型的数组转化成对象映射,可以使用Record来指定隐射对象的类型

在这里插入代码片
interface Person{
    name:string
    age:number
}
const personList = [
    {name:'jack',age:25},
    {name:'lucy',age:22},
    {name:'rose',age:18}
]
const personMap:Record = {}
personList.map((person)=>{
    personMap[person.name] = person
})

比如在传递参数时,希望参数是一个对象,但是不确定具体的类型,就可以使用Record作为参数类型

function doSomething(obj:Record){}

不可为空类型{NonNullable}

定义:type NonNullable = T extends null | undefined?never:T
从T中剔除null、undefined、never类型 不会剔除void,unkown类型

type T01 = NonNullable//string|number
type T02 = NonNullable<(()=>string),string[]|null|undefined>//(()=>string)}|string[]
type T03 = NonNullable<{name?:string,age:number}|string[]|null|undefined>//{name?:string,age:number}|string[]

构造函数参数类型(ConstructorParameters)

返回class 中构造函数参数类型组成的元组类型

// 定义
// Obtain the parameters of a constructor function type in a tuple
type ConstructorParametersany> = T extends new (...args:infer P)=>any?P:never;

// 使用
class Person{
    name:string
    age:number
    gender:'man'|'women'
    constructor(name:string,age:number,gender:'man'|'women'){
        this.name = name
        this.age = age
        this.gender = gender
    }
}
type ConstructorType = ConstructorParameters//[name:string,age:number,gender:'man'|'women']
const params:ConstructorType=['jack',12,'man']

实例类型

获取class构造函数的返回类型

定义:

// 使用

class Person{
    name:string
    age:number
    weight:number
    gender:'man'|'women'

    constructor(name:string,age:number,gender:'man'|'women'){
        this.name = name
        this.age = age
        this.gender = gender
    }
}

type Instance = InstanceType//person

const params:Instance={
    name:'jack',
    age:22,
    weight:120,
    gender:'man'
}

函数参数类型{Parameters}

获取函数的参数类型组成的元组

// Obtain the return type of a function type in a tuple
type InstanceTypeany>=T extends new (...args:any)=>infer R?R:any;

// 用法:
type FunctionType = (name:string,age:number)=>boolean
type FunctionParamsType = Parameters//[name:string,age:number]
const params:FunctionParamsType=['Jack',20]

函数返回值类型{ReturnType}

获取函数的返回值类型
定义

// Obtain the return type of a function type

type ReturnTypeany>= T extends (...args:any)=>infer R?R:any;

// 使用

type FunctionType = (name:string,age:number)=>boolean|string
type FunctionReturnType = ReturnType //boolean|string

总结

高级类型

  • & 交叉类型,将多个类型合并为一个类型,交集
  • | 联合类型 将多个类型组合成一个类型 可以是多个类型的任意一个,并集

关键字

  • T extends U 类型约束 ,判断T是否可以赋值给U
  • P in T 类型映射,遍历T的所有类型
  • parameterName is Type 类型谓词,判断函数参数parameterName是否是Type类型
  • infer P 待推断类型 ,使用infer标记类型P,就可以使用待推断的类型P
  • typeof v === ‘typename’ 原始类型保护,判断数据的类型是否是某个原始类型(number,string,boolean,symbol)
  • intanceof v 类型保护,判断数据的类型是否是构造函数的prototype属性类型
  • keyof 索引类型查询操作符 返回类型上已知的公共属性名
  • T[K] 索引访问操作符,返回T对应属性P的类型

映射类型

  • Readonly 将T中所有属性都变为只读
  • ReadonlyArray 返回一个T类型的只读数组
  • ReadonlyMap 返回一个T和U类型组成的只读Map
  • Partial 将T中所有的属性都变成可选类型
  • Required 将T中所有的属性都变成必选属性
  • Pick 从T中摘取部分属性
  • Omit 从T中排除部分属性
  • Exclude 从T中剔除可以赋值给U的类型
  • Extract 提取T中可以赋值给U的类型
  • Record 返回属性名为K,属性值为T的类型
  • NonNullable 从T中剔除null和undefined
  • ConstructorParameters 获取T的构造函数参数类型组成的元祖
  • InstanceType 获取T的实例类型
  • Parameters 获取函数参数类型组成的元祖
  • ReturnType 获取函数返回值类型

你可能感兴趣的:(typeScript,typescript,javascript)