2020-11-05Typescript(2.2)

2020-11-05  本文已影响0人  夏天的风2020

泛型 Generics---typeScript中最难的一部分

Adding simple type annotations //给我们的变量加一些简单的类型声明,
string类型/number类型/any类型

//泛型是怎么出现的,它要解决什么的问题?
//function echo(arg){
//   return arg
// }

//调用
//const result = echo(123)
//传入数字123,返回any类型,
//我们的变量丧失了类型,


//这样写就没啥问题了
function echo(arg: number): number{
   return arg
 }
const result = echo(123)
//但是我们可能传入其他类型,
//我们传入和返回的没办法统一,


泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,
而在使用的时候再指定类型的一种特性。

function echo<T>(arg: T): T{
   return arg
 }
const str: string = 'str'
const result = echo(123)
//传入类型,返回的也是string

//我们也可以完全不指定,这时候类型推论会帮我们做事情,
function echo<T>(arg: T): T{
   return arg
 }
const result = echo('str')
//类型推论会推断,str就是string类型,自然它返回的也是一个string类型,



泛型可以传入多个值,
新需求:我们有一个 tuple元组,里面有2个值,他们的类型都是随意的,
这时候我要返回一个新的tuple ,调换他们的位置,
//新建一个函数
function swap(tuple){
   return [tuple[1],tuple[0]]
 }
//当然会丧失他们的type,

//创建泛型,
function swap<T,U>(tuple: [T,U]): [U,T]{
   return [tuple[1],tuple[0]]
 }
const result2 = swap(['string',123])

//鼠标移至result2,显示const result2: [number,string],
//已经出现了它的类型,
//第0项我们可以把它当作number使用,result2[0].调用number上边的方法
//第1项我们可以把它当作string使用,result2[1].调用string上边的方法


本节总结
介绍了泛型的动机和它最简单的用法,
泛型,它就像一个占位符,或者是一个变量,
在使用的时候我们可以把定义好的类型像参数一样传入,
然后它可以原封不断的帮我们输出,

泛型 Generics---约束泛型

 //上节学习了泛型出现的动机要解决什么问题和简单用法
 //我们可以把它看成一个占位符,在使用的时候才动态的填入确定的类型值,

   //约束泛型
   function echoWithArr<T>(arg: T): T{
      console.log(arg.length)  //发现报错,Property 'length' does not exist on type 'T'
      return arg               //这个例子中,泛型T不一定包含属性length
    }

  因为在我们函数内部使用泛型变量的时候,由于事先不知道它是什么类型,
  所以不能随意的来操作它的属性和方法,

  //我们就决定我们这个函数应该是作用给一个含有T类型的Array,
  //这样length这个属性也就有了

  //T[]
  function echoWithArr<T>(arg: T[]): T[] {
      console.log(arg.length)  
      return arg              
    }

  //const arrs = echoWithArr([1,2,3]),
  //鼠标移至arrs,显示const arrs: number[]

  //但是这个解决方案不是完美的,我们只能传入数组
  //但是可能对象,甚至简单类型string都可以有length这个属性,
  //const arrs = echoWithArr('str') //会报错

  我们需要一个新的解决方案,我们可以对泛型进行一个约束,
  只允许函数传入那些包含length属性的变量,这就是约束泛型,

  interface IWithLength {
     length: number;
   }
  function echoWithLength<T extends IWithLength>(arg: T): T {
     console.log(arg.length)
     return arg
  }
  用extends关键字来约束传入的泛型,
  告诉它,你必须要有length这个属性,要不然会报错,

  //const str = echoWithLength('str')  //不会报错
  //鼠标移至str,显示const str:'str'

  //const obj = echoWithLength({length: 10})
  //鼠标移至obj,显示const obj:{length: number}

  //const arr2 = echoWithLength([1,2,3])
  //鼠标移至obj,显示const arr2: number[]


  //interface--Duck Typing
  只要你叫起来像鸭子,只要你有length属性,那么就可以符合这个约束,
  就没有问题,不管你是什么样的类型都可以,
  不管你是字符串,object,Array都没有问题,


  本节总结:
  着重讲了约束泛型,关键是在泛型中使用extends关键字,就可以让
  传入值满足我们特定的约束条件,而不是想传入什么就传入什么,

泛型 Generics---泛型在类和接口中的使用

//之前我们显示的泛型都是作用在函数中,在函数的参数和返回值中使用泛型,

//泛型在类上边应用,

我们创建一个对列类,
队列中有两个方法,一个是进入对列push(),二是离开对列pop()
对列是个先进先出的数据结构,所以我们使用了push()和shift()两个方法,
这时候我们就可以使用这个对列做一些事情,

class Queue {
   private data = []
   push(item){
     return this.data.push(item)
   }
   pop(item){
     return this.data.shift()
   }
 }

// const queue = new Queue()  //创建一个对列
// queue.push(1)
//queue.push('str')
//console.log(queue.pop().toFixed())  
//console.log(queue.pop().toFixed())  

//此时运行会报错
//queue.pop(...)toFixed is not a function

//为什么在我们的ts代码中没有抓到这个错误呢
//首先它允许你像对列中添加任何类型的数据,
//当然数据被弹出对列的时候也可以是任意类型,

//我们可以看到我们可以在里面添加string类型的数据,
//使用的时候就会出现无法捕捉的错误,
//比如这个例子,我们弹出的第二个类型是string类型,但我们调用了
//只有数字类型才有的方法,
//所以在typeScript中没有抓到这个错误,

//实际上该用法是假如只有number类型时才会被添加到对列里,
一个解决办法是添加的时候创建一个约束,

//   push(item: number){
//     return this.data.push(item)
//   }
//queue.push('str')  //此时会出现一个错误

//这是一个很坏的解决方法

假如当你想创建一个字符串的对列的时候,将不得不再次修改相当大类型的代码,
我们真正想要的是一种方法是无论什么类型被推入对列,被推出的类型都与推入的是一样的,
这时候就可以让泛型来帮助我们,我们可以创建一个泛型类,怎么写?
之前我们是在函数名称后面加<>,现在我们在类名称后面加<>,

class Queue<T>{
   private data = []
   push(item: T){   //希望被push进去的类型是T
     return this.data.push(item)
   }
   pop(): T{   //被推出的类型应该是T
     return this.data.shift()
   }
 }
//这时候我们创建了一个带有泛型的对列,一个类,

要初始化的时候,我们需要在Queue这个构造函数后面加上你想要的类型,
const queue = new Queue<number>()


//我们上边说类就可以用泛型来描述,
//interface--接口也可以接受泛型的洗礼,变得灵活起来,

泛型和interface,
定义一个interface叫KeyPair,它有两个值,一个叫key,一个叫value
我们希望key和value这两个值都是我们在使用的时候,动态的的传入,
现在我不确定它是什么类型,
所以皆可以给KeyPair定义两个类型,

interface KeyPair<T,U>{
   key: T;
   value: U;
 }

//let kp1: KeyPair<number,string> = {key:1, value:'str'}
//let kp2: KeyPair<string,number> = {key:'str', value:123}

//定义数组类型的时候,
//let arr: number[] = [1,2,3]

//现在我们可以使用表示泛型的形式来表示,
//let arrTwo: Array<number> = [1,2,3]

//以上就是interface搭配泛型以后可以灵活地返回不同的类型,


关于泛型的总结:
1.创建一个拥有特定类型的容器,比如类和interface上的泛型,
  仿佛给一个容器贴标签一样,
  let arrTwo: Array<number> = [1,2,3]  
  给Array贴上了number标签,告诉arrTwo我要你是一个装满number类型的数组,

 const queue = new Queue<number>()
  或者告诉一个类我希望你是一个装着数字的对列,
  甚至你可以想它像一个可变的参数那样,在用的时候传入,生成一个不同类型的
  一个容器,


2.可以用它来灵活的约束参数的类型,不需要参数是个特别死板的类型,
  比如说,我们不希望它是一个特定string类型,不希望它是一个number类型,
  而我要传入的参数必须有某某属性,某某方法,否在就会报错,


//(第一节:generrics,ts)
3.在函数使用的时候,函数的类型推断不会流入到函数体内,
所以使用表达式没法明确建立类型的绑定,
用泛型可以让我们打破这个鸿沟,
function echo<T>(arg: T): T{
   return arg
 }
const result = echo(true)  //result就会返回它传入的类型,
上一篇 下一篇

猜你喜欢

热点阅读