likes
comments
collection
share

Day56:组件库封装-TypeScript入门

作者站长头像
站长
· 阅读数 17

配置

安装tsc工具进行编译 npm i typescript -g

查看版本号:tsc -v

编译ts代码-需要使用tsc编译之后才能运行,TS为JS的衍生,浏览器不能直接识别TS语法:tsc xxx.ts

运行ts代码:node xxx.jsDay56:组件库封装-TypeScript入门 或者直接运行ts代码——ts-node xxx.ts

解除2584报错:

tsc --init

在生成的配置文件中的lib中,添加['DOM','ESNext']

一、基础类型

  1. srting
  2. number
  3. Boolean
  4. undefined
  5. null
  6. any:可以定义任何类型,但是相当于关闭了类型判断,所以不建议使用
//类型注解
let str: string = '123'
let num: number = 123
let bol: boolean = false
let und: undefined = undefined
let nul: null = null
//尽量避免在ts中使用any 使用any就是关闭类型检查 
let a: any = true

二、类型推断与void类型

  1. 直接定义且赋值,ts会自动判断赋值的类型
  2. void类型,返回没有返回值的类型
  3. never类型,表示永远也无法达到的类型(比如报错
  4. unknown未知的类型,数据来源于外部(后端接口)
//类型推断
let str1 = 'haha' //自动推断出来了str1是字符串类型
//void类型 返回没有返回值的类型
function fn(a: string): void {
    console.log(a)
}
//never类型 表示永远无法达到的类型
function err(): never {
    throw Error('wrong')
}
//unknown 未知的类型 数据来源于外部(后端接口)
let res: unknown
res = {}
export { }

三、对象,数组,函数

  1. Object/{}表示的是基类,object表示的是普通对象
  2. 定义数组时可以给数组指定类型arr:string arr:number
  3. 或者使用Array来指定数组的内容类型
  4. 数组也可以使用any类型来兼容数组中不同类型的数组元素(不推荐)
  5. 方法函数在写的时候需要给每一个传入参数和return结果规定类型
let obj: object = { a: 1, b: 2 };
let arr: string[] = ["1", "2", "3"];
let arr1: number[] = [1, 2, 3];
let arr2: Array<boolean> = [false, true]
let arr3: any[] = [1, '2', true]
function fn(a: string, b: string): number {
    return Number(a) + Number(b)
}
fn('1', '2')
export { }

四、字面量

  1. 数字,字符串,布尔值类型
  2. 只能给字面量类型赋值字面量这个值,不能使用其他值
  3. 定义一个eat函数,他只能接收参数food,food值只能是rice noodle中的一个
  4. 联合类型function eat(food:'rice'|'noodle'),只要是其中一个即可
  5. 定义一个联合定义,可以使用type类型别名
type x = string | number
function add(a:x,b:x):number {
  return Number(a) + Number(b)
}
add(2,'3')
  // 联合类型food 
  type f = 'rice'|'noodle'
// 方法eat
function eat(food:f) {
  console.log(food)
}
eat('rice') // 只能传入'rice'|'noodle'其中之一的值
  1. 类型别名常用来定义对象leixing
type obj = {
  a:number;
  b:string
}  
let p:obj = {
  a:666,
  b:'777'
}
  1. 在ts中,如果需要换一种类型,都可以使用类型别名这种形式

五、枚举类型

很多时候,希望将一些字面量的联合类型列举起来,可以枚举的数据类型

  1. 定义一个eat方法,可以吃的东西——苹果,香蕉,桃子。枚举类型——联合列举字面量类型
enum Fruit = {
  APPLE = 'apple',
  BANANA = 'banana',
  PEACH = 'peach'
}
function eat(food:Fruit) {
  console.log(food)
}
eat(Fruit.APPLE)
  1. 枚举类型enum一般使用大写开头
  2. 枚举一般分为字符串枚举和数字枚举
  3. 枚举中有一个APPLE属性,它的值为'apple'
  4. 给eat传入的实参必须满足三中类型之一
  5. 代码语言中,经常会使用数字来表达状态。xhr.readyState监听接口是否正确返回数据(0=>4)
enum State {
  // 不赋值的话,默认赋值从零开始依次递增
  BEGIN,
  READY,
  GET,
  HANDLE,
  RESPONSE
}
if(xhr.state = State.BEGIN) {
  '请求未发送'
}else if(xhr.state = State.READY) {
  '请求准备发送'
}...

六、接口

接口,接口描述对象或者函数

  1. 定义Animal,类型上有字符串name,数字age,联合类型 男|女 gender
  2. 接口有专用关键字interface
  3. 使用接口进行对对象类型的描述
interface Animal {
  name:string;
  age:number;
  gender:'男'|'女'
}
let a1:Animal = {
  name:'peiqi',
  age:3,
  gender:'女'
}
a1.gender
  1. 定义对象类型时分隔符为分号
  2. 与类型别名的区别
    1. 类型别名有=赋值,接口没有
    2. 接口可以扩展,再interface一次Animal,再给它增加一组key,value,并不是覆盖原有类型,而是在原有类型基础上增加一个类型
interface Animal {
  height:number
}  
  1. 由于接口的可扩展性,所以通常使用接口来描述对象
    1. 类型别名想要扩展需要使用交叉类型(和& 联合类型为或|)
    2. 定义两个类型别名,在第三个类型别名中交叉两个类型别名1&2,即可扩展,但很麻烦
  1. 接口可以继承
    1. 如果定义一个新的接口Dog,它可以继承自Animal,使用extends进行继承,dog就会拥有所有Animal的属性
 interface Dog extends Animal {
  bark():void; /* 定义bark方法,没有返回值 */
} 
let a3:Dog = {
  name:'peiqi',
  age:3,
  gender:'女',
  height:100,
  bark() {
    console.log('wangwang')
  }
}
// 调用a3,a3.之后就会有a3的属性弹出
    1. 类型别名无法继承
  1. 实际应用中,对象是通过构造函数(类)来生成的,并不是直接通过字面量形式生成。类可以实现接口
    1. 类是如何实现接口的:使用implements进行类对于接口的实现
class MyDog implements Dog {
  name: string
  age:number
  gender:'男'|'女'
  height:number
  constructor(
    _name:string;
    _age:number;
    _gender:'男'|'女',
    _height:number,
  ){
  this.name   = _name
  this.age    = _age
  this.gender = _gender
  this.height = _height
  }
  bark(): void{
    console.log(this.name)
  }
}
let a4:MyDog = new MyDog('xiaohei',4,'女',40)
a4.bark()
    1. 在类中定义——如果添加具体属性,需要先声明一下(ts中添加任何属性都必须声明。继承可以不用重新声明,但是class MyDog implements Dog这种必须要重新声明)
    2. 类中定义成员:普通属性应该定义在Constructor上,方法应该定义在原型身上
    3. 前面的age是定义的对象身上的属性,后面一个是传入的参数
    4. constructor后面进行的是传入的参数,是用来描述实参的形参
    5. this后面的表示要给成员添加什么样的属性。
    6. 方法是定义在原型上的,不用写this
    7. 前后定义的类型必须一致,否则无法这样赋值。
  1. 接口的索引访问:如果想要某一个索引不写值也可以运行,使用?,?表示这个索引可以有也可以没有
  2. Phone中有两个必须的索引,onSale是可选的索引,props是可以添加的自定义索引
interface Phone {
  title:string;
  price:number;
  onSale?:boolean // ?表示这个索引可以有也可以没有
  [props: string] : any // 可以添加任意多的索引,索引类型可以自己定义
}
let p1:Phone = {
  title:'iphone15',
  price: 5999
}
let p2:Phone = {
  title:'iphone15',
  price: 5999,
  onSale: false
}
let p3:Phone = {
  title:'iphone15',
  price: 5999,
  weight: 400
}
  1. 索引访问类型:可以访问索引类型。
// 11-索引访问类型
type s = Phone['price'] /* s为number */
// 12-访问Phone中的索引
type k = keyof Phone
let k1:k = 'onSale'
let k2:k = 'price'
// 13-接口描述函数
interface Fn {
  (a:number):number /* 参数为数字,返回值也是数字。接口描述函数用冒号链接 */
}
let fn:Fn = (a:number):number => {
  return a
}
fn(123
  1. 收集到的phone这个接口上所有的索引值字面量组成的联合类型,只能访问Phone中的索引
  2. 接口描述函数:定义接口,实现接口,调用接口

七、元组

ts中元组可以理解为固定了长度和类型的数组(长度为3,类型分别为字符串,数字,布尔)

可变元组:如果只需要最后三个元素为[string, number, boolean]

// 1
let turple:[string, number, boolean] = ['haah', 666, true]
// 2
let t2:[...any[], string, number, boolean] = [{}, 188, 'eee', 'haah', 666, true]

八、类型断言

类型注解,类型推断前面写了,类型断言:

let b: number | string = '123'
let len:number = (b as string).length

b既可以是字符串类型也可以是数字类型,如果想访问b的length类型——如果b为number类型,则length不存在,只有b为string类型时,才可以访问b身上的length——当一个变量可能存在多个类型时,可以使用断言来缩小它的类型。将b断言为字符串类型,即可访问length

九、泛型

ref定义的变量上肯定有一个value属性,value应该是什么类型呢?需要一个什么都可以的类型,但绝对不能是any

interface Ref<T> {
  value: T // 定义Ref接口和定义了任意一个类型T,其实写什么都行
} 
let r1:Ref<string> = {
  value:'haha'
}
let r2:Ref<number> = {
  value:321
}
// fn传入的参数和返回的参数是一个类型
function fn<K>(a:K):K {
  return a
}
fn('haha') 
fn(123)

定义Ref接口和定义了任意一个类型T,其实写什么都行。这样定义的类型可以传入任意类型

在使用接口的时候,需要定义需要传入的类型,将泛型具体化。相当于泛型是一个形式参数,泛型是用来传递类型的。在具体使用的时候需要传入实际参数(具体的类型)

如果在定义方法的时候定义它需要传入和返回同一种类型的参数,则在实际使用的时候传入什么样的参数,则传入和返回的都是这个参数的类型

练习

实现一个类ArrayList,这个类具有数组的添加元素(add)和访问数组(get)的功能。利用泛型来实现这样一个类

class ArrayList<T> {
  arr:T[] // 定义一个泛型空数组
  index: number = 0 // 表示数组元素的个数,不表示索引
  constructor(){
    this.arr = [] // 开始时置空数组
  }
  // 添加元素
  add(element:T){
    this.arr[this.index++] = element // 先执行再index递增,添加了元素就增加长度
  }
  // 访问元素
  get(index:number){
    return this.arr[index] ? this.arr[index] : undefined
  }
}
let arr1 = new ArrayList<number>()
arr1.add(1)
console.log(arr1.arr) // [1]
arr1.add(2)
console.log(arr1.arr) // [1,2]
console.log(arr1.arr[1]) // 2
console.log(arr1.arr[2]) // undefined

泛型和泛型约束

写一个方法,可以访问对象身上的属性

function getProperty<T extends object,K extends keyof T>(obj:T,key:K){
  return obj[key]
}
getProperty({a:1,b:2},'a') // key只能是a或者b,

obj表示整个对象,obj的T extends object约束T为普通对象类型。key的K约束为K extends keyof T,在给obj给了内容后,key只能是obj的key