TypeScriptp基础用法

1.TypeScript 的介绍

TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码

2012年10月,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript

TypeScript的作者是安德斯·海尔斯伯格,C#的首席架构师。它是开源和跨平台的编程语言

TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。

TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。

TypeScript是JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持*,它由 Microsoft 开发,代码开源于 GitHub 上。

2.TypeScript 的特点

TypeScript 主要有3 大特点:

始于JavaScript,归于JavaScript:

TypeScript 可以编译出纯净、简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Nodejs 环境中和任何支持 ECMAScript 3 (或更高版本)的JavaScript 引擎中。

强大的类型系统:

类型系统允许 JavaScript 开发者在开友JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。

先进的 JavaScript:

TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立壮的组件。

总结

TypeScript 在社区的流行度越来越高,它非常适用于一些大型项目,也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。

3.安装TypeScript:

在终端输入一下命令:

npm install -g typescript

查看是否安装成功:

tsc -v

4.TS文件编译JS文件:

1.ts文件支持直接写js代码:

// 1.ts支持js代码,在ts中可以直接写js代码,一般浏览器也只支持没有写ts语法的ts文件,如果写了ts语法,那么浏览器将不在支持ts文件,此时就要引入ts编译后的js文件
(()=>{
  function testTs(str) {
    console.log(str)
  }
  testTs('hello word')
})()

2.将ts文件编译为js文件:

在终端执行tsc ts文件名,如:

tsc test.ts

执行完以上命令后会在对应的ts文件所在文件夹中生成一个相同名称的js文件。

5.自动编译TS文件:

1.在项目根目录下打开终端执行以下命令生成tsconfig.json配置文件:

tsc --init

2.配置配置文件如下:

"outDir": "./js",       /* 1.将ts编译好的js文件放到当前项目下js目录中 */
"strict": false,       /* 2.取消严格模式 */

3.启动监视任务,终端运行:

vscode下—终端—运行任务—显示所有任务—tsc监视tsconfig.json

4.测试是否生效,在编辑器中编写ts代码,此时可以看到js文件自动变化了。

6.类型注解:
类型注解专门用来约束函数或者变量,限制了变量的数据类型:

(()=>{
  // 对str形参做类型注解:要求传入实参的数据类型为字符串,那么实参只能是字符串,如果是其他数据类型,那么将报错。
  function testTs(str: string) {
    console.log(str)
  }
  testTs('hello word')
  let str = '你好'
  testTs(str)
  // testTs(123) 报错
  // testTs(true) 报错
  // testTs([1,2]) 报错
  // testTs({}) 报错
})()

7.基本类型:

TypeScript支持JavaScript提供的所有数据类型,此外还提供了使用的枚举类型。

(()=>{
  // 关键字 变量名:数据类型 = 值
  // 1.布尔类型:
  let flag:boolean = false
  console.log(flag)

  // 2.数字:TypeScript里面和JavaScript一样,里面所有数字都是浮点数,这些浮点数类型都是number,除了支持十进制和十六进制字面量,还支持二进制和八进制。
  let num1:number = 10
  let num2: number = 0b1010 // 二进制
  let num3: number = 0o11 // 八进制
  let num4: number = 0xa // 十六进制

  // 3.字符串:TypeScript中字符串和JavaScript中一样,可以使用单引号也可以使用双引号:
  let namges:string = 'kuhai123'

  // 4.undefined和null: undefined和null可以作为其他类型的子类型,也就是说undefined和null可以赋值给其他类型的变量(前提关闭ts配置文件中的严格模式),而其他类型的数据不能赋值给其他不是该变量对应类型的变量
  let nud: undefined = undefined
  let nul: null = null
  num1 = undefined

  // 5.数组:规定了数据类型
  let arr1: number[] = [1,2,3]
  let arr2: Array<string> = ['1','2','3'] // 泛型定义数组
  // 元数组:上面的数组规定只能用一种数据类型填充,如果要填充其他数据类型,那么就要使用元数组,元数组前面可以定义多个类型,但是后面要和前面一一对应:
  let arr3: [string,number,boolean] = ['1', 2, false]

  // 6.枚举:一个固定且元素的个数不变的数据类型,每个元素都有自己的编号,可以理解为索引
  enum Color {
    red, // 这里可以给每一个元素指定编号,如:red = 100,如果这里指定编号为100,那么它的下一个元素在没有指定编号的情况下会在前一个的基础上递增1,指定的话就是按指定的。
    blue,
    green
  }
  let redIndex = Color.red // 这里redIndex的值打印为0,因为枚举直接点出来的为编号,如果想要拿到枚举的值就要通过[编号]
  let redValue = Color[0] // red

  // 7.any类型:此类型可以存储任何类型的数据,当一个数组中不知道有多少个类型时可以使用此类型,但是也有一些不利之处,比如数组中存了数字类型,却用了字符串api,此时不会提示错误
  let anyStr: any = 100
  anyStr = 'hello'
  anyStr = true
  anyStr = [1,2,'2']
  anyStr = Color

  // 8.void类型:void与any类型相反,表示什么类型也没有,当一个函数没有返回值时可以使用void:
  function sayHello(): void{
    // return 'hello',使用了void后,就不能有返回信息,当然后面可以返回什么也没有、返回null、返回undefined
    console.log('hello')
    return
    // return null
    // return undefined
  }
  function sayHai(){
    return 'hai'
  }
  // 要求返回数字类型:如果指定了返回类型,不返回或者返回类型不符合则报错
  function sayNubmer(): number{
    // console.log('1')
    return 0
  }

  // 9.object类型:对象类型:
  function getObj(obj: object): object{
    return {
      name: 'kuhai123',
      age: 18
    }
  }
  let obj = {
    name: 'kuhai123'
  }
  getObj(obj)

  // 9.联合类型:表示从多种类型中取一种类型,多个数据类型之间用|分开
  let str: number | string = 99
  str = 'hello'

  // 10.类型断言:类型断言类似java中数据类型转换,在ts中当某个变量时联合类型时,调用某个数据类型特有的api时,此时ts有可能报提示错误,那么就告诉它,当变量为某种类型时在执行就不会有报错提示了
  function testHandle(str: number|string){
    // if (str.length) { // 因为这里str可能为数字类型,而数字类型没有length方法,所以ts会报错,此时可以使用ts的类型断言,类似java数据类型强制转换,如:
    if ((<string>str).length) {
      // return (<string>str).length
      return (str as string).length
    } else {
      str.toString().length
    }
    // 类型断言语法有两种:1.<数据类型>变量  2.变量 as 数据类型
  }

  // 11.类型推断:ts在没有明确指定类型的情况下,会推测出一个类型:
  let str2 = 'hello'
  console.log(str2) // 鼠标移动到变量上面可以看到是string类型,它是自动推测的
  // str2 = 200 // 字面量的方式赋值会自动给一个数据类型,再次改变其他类型会报错,而先申明一个变量,后赋值的即使改变数据类型也不会报错
  let str3 // 被推断为any类型:
  str3 = 'hellow'
  str3 = 123
})()

8.接口:

接口也是一种约束,一个接口的属性在某个对象中都有出现这个是符合接口的对象,如果一个接口中的某个属性不在这个对象中,那么这个对象不符合接口,对象中除了接口中定义的所有属性还有其他属性,这个对象也是符合接口的。

接口描述一个拥有 firstName和lastName字段的对象。在Typescript里,只在两个类型内部的结构兼容,那么这两个类型就是兼容的。这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用implements语句。

(()=>{
  // 定义一个PersonStand接口:该接口可以用来限制参数字段个数及字段
  interface PersonStand {
    readonly name: string, // readonly修饰为只读字段
    age: number,
    weight?: number // 字段后面跟问号表示可有可无
  }
  function testTs(person: PersonStand) {
    // 使用了接口作为类型注解,下面是用接口的属性时会自动提示字段
    let result = person.name + '-' + person.age
    console.log(result)
  }
  // 定义一个符合PersonStand接口的对象:
  let obj = {
    name: '苦海123',
    age: 18
  }
  // 定义一个不符合接口PersonStand的对象:这里多的字段并不影响,但是少的字段或者对不上的字段会报错:
  let obj2 = {
    name: '苦海123',
    age: 18,
    height: 180
  }
  let obj3 = {
    name: '苦海123',
    ageNumber: 18
  }
  testTs(obj)
  testTs(obj2)
  // testTs(obj3) 报错

  // 接口约束对象:接口约束对象时,对象的属性和接口中的字段必须保持一致,包括数量及名称和类型,否则都会报错,不过有特殊声明,如只读,可有可无
  const person:PersonStand = {
    name: '苦海123',
    age: 18,
    // id: 0 // 接口约束对象时,对象的属性和接口中的字段必须保持一致,包括数量及名称和类型,否则都会报错
  }
  // person.name = 'kuhai123' // PersonStand接口中name字段为只读字段,不允许被修改,这里修改会报错


  // 泛型接口:在定义接口时,为接口中的属性或方法定义泛型类型,在使用接口时,再指定具体的泛型类型,也就是说当定义一个接口时,某个属性或方法不确定时,此时可以使用泛型:
  // 定义一个增删改查的泛型接口:
  interface InterFaceCrud<T>{
    data:Array<T>
    add:(a:T) => T
    queryId:(id: number) => T
  }
  // 定义一个用户信息类:供泛型接口使用
  class User{
    id?: number
    name: string
    age: number
    constructor(name:string,age:number){
      this.name = name
      this.age = age
    }
  }
  // 定义一个实现类,实现接口:
  class PersonCrud implements InterFaceCrud<User>{
    data: Array<User> = []
    add(user: User):User {
      user.id = Date.now() + Math.random()
      user.name = 'kuhai'
      user.age = 18
      this.data.push(user)
      return user
    }
    queryId(id: number):User {
      return this.data.find(user => user.id == id)
    }
  }
  // 实例对象:
  const kuhai:PersonCrud = new PersonCrud()
  kuhai.add(new User('苦海',18))
  kuhai.add(new User('kuhai123',18))
  console.log(kuhai.data)
})()

9.函数类型及函数:

在ts中,函数也可以有自己的类型,只不过它需要借助接口才能实现。

(()=>{
  // 函数类型:通过接口的方式作为函数的类型来使用,此时接口中不能在像之前参数一样使用属性来约束,而是使用调用签名约束
  // 调用签名:具有参数列表和返回类型的函数定义:参数列表里面的每个参数都要名字和类型
  // 定义一个接口:
  interface regSearchFun {
    // 定义一个调用签名:要求传入两个字符串类型参数,并返回布尔值
    (str1: string, str2: string): boolean
  }
  // 定义函数并使用接口:此时函数形参类型和接口中要一致
  const serarchHandle:regSearchFun = function(s1: string, s2: string) {
    // return s1 + s2 // 这里会报错,因为没有返回布尔值,不遵循regSearchFun接口规范
    return s1.search(s2) > -1
  }
  serarchHandle('hello','e')



  // 函数的声明方式:和js基本一样,只是多了参数类型限制等:
  // 命名函数:
  function sum(x: number, y:number) {
    return x + y
  }
  // sum(1, '2') // 报错,参数类型不合法
  sum(1, 2)

  // 匿名函数,函数表达式:
  let sumXy = function(x: string, y:number):string{
    return x + y
  }
  sumXy('id=',12)

  // 函数完整写法:
  // sumAd表示变量名
  // 函数的类型:(x:number,y:number) => number ,箭头前面表示入参类型,后面表示返回参数类型
  const sumAd: (x:number,y:number) => number = function (x:number,y:number):number{
    return x + y
  }
  // sumAd(1,'2') // 报错
  sumAd(1,2)

  // 箭头前面写了类型后,后面函数的类型可以省略,省略也是会校验参数是否合法的
  const sumAdTwo: (x:number,y:number) => number = function (x,y){
    return x + y
  }
  // sumAdTwo(1,'2') // 报错
  sumAdTwo(1,3)



  // 函数的可选参数与默认参数:默认参数:形参:类型='默认值'、可选参数:形参?:类型
  function getNameStr(firstName:string = '陈',lastName?:string) {
    console.log(firstName+'-'+lastName)
  }
  getNameStr('陈','辉')
  getNameStr('陈') // 陈-
  getNameStr() // 陈-


  // 剩余参数(rest参数):和arguments基本一样
  // ...plist:string[]表示:剩余的参数放在了string类型的数组plist中,
  function getMoreParam(p1: string,...plist:string[]) {
    // console.log(arguments)
    console.log(plist) // 前面的形参接收完后剩下的就是在剩余参数中
  }
  getMoreParam('a','b','c') // ['b','c']


  // 函数重载:函数名相同,函数的参数类型及个数不同,但是和java中方法重载还是有很大区别,ts中方法重载只需要写一个方法的方法体,其余都是只写类型,不能多写方法体,否则报错,业务逻辑只能在最后一个方法体里面通过判断参数类型做相应处理:
  function getSum(a: string,b: string): string
  function getSum(a: number,b: number): number
  
  function getSum(a: string | number,b: string | number): string | number {
    if (typeof a == 'string' && b == 'string') {
      return a + b
    } else if (typeof a == 'number' && b == 'number') {
      return a + b
    }
  }
  getSum(1,5)
  getSum('2','6')
  // getSum(3,'6') // 报错



  // 泛型:在定义函数、接口、类的时候不能预先确定要使用的数据的类型,而是在使用函数、接口、类的时候才能确定数据的类型,如:定义一个方法,将某个数据存到数组中,数组的长度也可指定:
  function createArray<T>(str:T, count: number): T[] { // T就是一个变量,这里可以写任意非关键字字符串,关注在调用时写真实数据类型
    let arr:Array<T> = [] // 也可写为:let arr: T[] = []
    for (let i = 0; i < count;i++) {
      arr.push(str)
    }
    return arr
  }
  // createArray<number>('3',5) // 报错
  createArray<number>(5,5)
  createArray<string>('2',5)

  // 多个泛型参数函数:有的时候可能需要多个类型作为参数,且每个参数数据类型不确定,此时可以使用多泛型,如:
  function getMsg<X,Y>(a:X, b:Y): [X,Y] {
    return [a,b]
  }
  getMsg<number,string>(100,'分')
  // getMsg<string,number>(100,'分') // 报错
  getMsg<string,number>('分数为:',100)
})()

10.类:

(()=>{
  // 定义一个接口:
  interface PersonStand {
    name: string,
    age: number
  }
  // 定义一个类:类,可以理解为模板,通过模板可以实例化对象
  class PersonClass {
    // 公共属性:
    name: string
    age: number
    weight: number
    // 定义一个构造器:实例化对象的时候可以直接对属性进行初始化
    constructor (personName: string = '苦海123', personAge: number,personWeight: number) {
      // 给属性赋值:
      this.name = personName
      this.age = personAge
      this.weight = personWeight
    }
    // 定义实例方法:
    sayHi(){
      console.log('hi, my name is '+ this.name)
    }
  }
  // 实例化一个对象:
  const personOne:PersonClass = new PersonClass('苦海123',18,55)
  // 定义一个函数要求传入符合PersonStand的对象,如果要满足PersonStand接口,那么定义的PersonClass类中的字段要包含PersonStand接口中所有字段,否则定义的类不符合接口标准
  function testTs(person: PersonStand) {
    // 使用了接口作为类型注解,下面是用接口的属性时会自动提示字段
    let result = person.name + '-' + person.age
    console.log(result)
  }
  testTs(personOne)




  // 继承:类与类之间的关系,继承后类与类的叫法,a类继承了b类,a类被称为子类(派生类),b类被称为基类(超类,父类)
  // 定义一个学生类继承于PersonClass类:使用关键字extends实现继承:
  class StudentClass extends PersonClass {
    // 学生特有的属性:
    score: number
    // 定义构造器时:首选接收完父类所需要的形参外,再加上自己类的属性所需形参
    constructor(personName: string, personAge: number,personWeight: number, score: number){
      // 构造器首行调用super方法,super方法会调用父类的构造器
      super(personName, personAge, personWeight)
      // 一定要掉完super后才可以进行其他赋值等操作:
      this.score = score
    }
    // 定义一个方法:
    sayHello(){
      console.log('hello, ' + this.score)
      // 子类中调用父类中方法:使用super关键字
      super.sayHi()
    }
    // 定义方法:
    sayHi(){
      console.log('hi, my name is '+ this.name)
    }
  }
  // 实例一个继承了类的类:
  const studentOne = new StudentClass('苦海123',18,60,99)
  console.log(studentOne.score)
  studentOne.sayHello()


  // 多态:父类型的引用指向了子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
  // 父类型的引用指向子类型对象:变量类型为父类,new的对象为子类
  const studentTwo:PersonClass = new StudentClass('苦海123',18,60,99)
  studentTwo.sayHi() // 其中名字字符为:苦海123
  const studentThree:PersonClass = new StudentClass('kuhai123',18,60,99)
  studentThree.sayHi() // 其中名字字符为:kuhai123,上面两个实例都调用了相同的方法,但是产生了不同的结果


  

  // 类的共有私有修饰符:主要描述类中的成员(属性、构造函数、方法)的可访问性
  // 定义一个动物类:属性、构造函数、方法前面不写修饰符其实默认是有修饰符public,表示公开的,它可以使属性在内里面或者外面都可以访问
  class Animal {
    readonly name: string // 默认前面public,如:public name: string,readonly表示只读属性,在对象实例化后(构造器中)就不能再被修改了
    private home: string // private修饰符表示私有的,只能在类里面使用,不能在此类外面使用,也就是说子类中也是无法访问
    protected age: number // protected修饰的成员在子类和本身类中可以访问,在外面无法访问
    constructor(names: string,homes: string){
      this.name = names
      this.home = homes
    }
    eat(){
      console.log(this.name + '吃东西,在' + this.home)
      console.log(this.age)
    }
  }
  // 实例对象:
  const cat = new Animal('猫猫','猫舍')
  console.log(cat.name) // 猫猫,在类的外部可以访问类的属性
  cat.eat() // 猫猫吃东西,在猫舍,在类的里面可以访问类的属性
  // console.log(cat.home) // 报错,被private修饰的成员在类外部是不能被访问的
  // console.log(cat.age) // 报错,protected修饰的成员在外面无法访问

  class Dog extends Animal {
    constructor(names: string,homes: string, readonly type: string = 'animal') { // readonly修饰构造器中的参数时,那么将会把参数默认给到类作为属性使用,此时外部也是无法修改其属性值的,public修饰的话将有一个public的属性,依次类推
      super(names,homes)
    }
    sayName(){
      console.log(this.name) // 可以访问,public修饰的成员在子类及外面也可以访问
      // console.log(this.home) // 报错,private修饰的成员只能在它本身类访问,外面和子类无法访问
      console.log(this.age)
    }
  }
  const dog = new Dog('狗狗','狗舍')
  console.log(dog.name) // 可以访问
  console.log(dog.type) // animal,构造器中使用readonly
  // dog.name = '小黄' // 属性被readonly修饰后,实例化对象后不能再被修改属性值
  // console.log(dog.home) // 报错,无法访问
  // console.log(dog.age) // 报错,protected修饰的成员在外面无法访问



  // getters和setters:存取器,可以有效的控制对象成员操作:如:定义一个只有姓氏和名字两个属性的类,并从其创建的对象获取姓名和设置姓名:
  class FirstLastName {
    firstName: string
    lastName: string
    constructor(f: string, l: string){
      this.firstName = f
      this.lastName = l
    }
    // 读取器,得到全名:
    get fullName(){
      return this.firstName + this.lastName
    }
    // 设置器,设置全名:
    set fullName(str){
      this.firstName = str.slice(0,1)
      this.lastName = str.slice(1)
    }
  }
  // 实例化对象:
  const chenhui = new FirstLastName('陈','辉')
  console.log(chenhui.fullName) // 陈辉, 获取
  chenhui.fullName = '苦海' // 设置
  console.log(chenhui.fullName) // 苦海



  // 静态成员:在类中通过static修饰的属性或者方法,属性则被称为静态属性,方法则被称为静态方法,使用他们的时候可以直接通过:类名.成员的名称
  class PersonThree {
    // 被static修饰的属性称为静态属性,
    static names: string = 'kuhai123'
    constructor (n: string) {
      // this.names = n // 静态属性不能通过构造函数对其赋值
    }
    static sayHi (){
      console.log('hello')
    }
  }
  const peronThree = new PersonThree('苦海')
  // console.log(peronThree.names) // 不能通过实例对象获取属性值,可以通过类名称直接点获取属性值
  console.log(PersonThree.names)
  PersonThree.names = '苦海123' // 可以通过类名直接修改静态属性值
  // peronThree.sayHi() // 不能通过对象调静态方法,静态方法也需要通过类名称调用
  PersonThree.sayHi()



  // 抽象类:用来概括某一类实物,比如动物吃东西,不同的动物吃的可能不太一样,此时可以定义一个抽象方法,只能知道有吃这个动作,等到子类继承时候在具体覆盖重写这个吃的方法,抽象类中包含抽象方法和实例方法,抽象类不能被实例化,它是供子类使用的,声明抽象类时只需要在class前面加关键词:abstract
  abstract class AnimalClass {
    // 抽象属性:抽象属性不能给默认值,否则会报错
    abstract names: string
    // 抽象方法:抽象方法后面不能给方法体,否则报错,这个方法应该是继承的子类需要被覆盖重写此方法的
    abstract eat()
    // 实例方法:
    sayHi(){
      console.log('动物嗷嗷叫了')
    }
  }
  // const Cat = new AnimalClass() // 不能直接new抽象类
  // 定义一个派生类继承抽象类:
  class Cat extends AnimalClass {
    // 派生类必须覆盖重写抽象类中所有的抽象方法及属性
    names: string = '猫猫'
    eat() {
      console.log('吃骨头')
    }
  }
  // 通过派生类使用抽象方法和属性:
  const catOne = new Cat()
  console.log(catOne.names)
  catOne.eat()
  catOne.sayHi()



  // 泛型类:
  class PersonTclass<T>{
    defaultValue: T
    print: (x:T) => T
  }
  // 实例化一个对象:
  const p1:PersonTclass<number> = new PersonTclass<number>()
  p1.defaultValue = 5
  p1.print(1)
  const p2:PersonTclass<string> = new PersonTclass<string>()
  // p2.print(1) // 报错
  p2.print('3')
})()

11.类类型:

ts可以和Java等语言一样,也可以约束一个类去符合某个条件(接口)。

(()=>{
  // 类类型:类的类型可以通过接口实现:

  // 定义一个接口:
  interface sayInf {
    // 定义一个方法:方法没有实现
    say()
  }

  // 定义一个类实现接口:
  class Person implements sayInf {
    // 这里需要重写say方法,类似java中接口的方法重写
    say() {
      console.log('hello')
    }
  }

  // 创建对象:
  const personOne = new Person()
  personOne.say()



  // 一个类也可以通过多个接口进行约束:多个接口之前用逗号隔开
  // 再定义一个接口:
  interface eatInf {
    eat()
  }
  // 在定义一个实现类实现前面两个接口:
  class PersonAther implements sayInf,eatInf {
    say() {
      console.log('hello')
    }
    eat() {
      console.log('吃饭')
    }
  }
  // 创建对象:
  const personTwo = new PersonAther()
  personTwo.eat()
  personTwo.say()



  // 接口继承接口:除了一个类可以遵循多个接口外,接口之前还可以相互继承,也就是说,一个接口可以继承其他接口,继承了其它接口的这个接口将拥有其它接口都有的约束条件:
  interface totalInf extends sayInf,eatInf {
    play()
  }
  class PersonThree implements totalInf {
    play() {
      console.log('玩')
    }
    say() {
      console.log('hello')
    }
    eat() {
      console.log('吃饭')
    }
  }
  const personThree = new PersonThree()
  personThree.play()
})()

12.对泛型进行约束:

(() => {
  // 有的时候,我们对泛型是有要求的,此时我们可以定义一个接口来约束泛型,如:number类型没有length属性,我们可以定义一个接口,要求它有length属性的类型:
  function getLength<T>(x: T):number {
    // return x.length // 报错,这里不知道x是什么数据类型,因此无法直接调用length方法,此时可以使用泛型约束,如:
    return 0
  }
  // 定义一个接口进行约束:传入的泛型必须有length属性才行:
  interface Ilength{
    length: number
  }
  // 再次尝试:泛型继承于接口:
  function getLengthSecond<T extends Ilength>(x: T):number {
    return x.length // 此时不会在报错
  }
  getLengthSecond<string>('hello') // string类型有length属性,所以可以
  // getLengthSecond<number>(123) // 报错,number类型没有length属性,所以报错
})()

13.申明文件:

有的时候我们ts项目中会引入其他的库,如jQuery,此时jQuery等第三方库默认是没有代码提示和补全等的,想要代码提示就得申明文件,如:

// 定义声明文件类型:此段代码可以单独建一个ts文件(jQuery.d.ts),此代码会自动扫码jQuery提示信息,申明文件在第三方库都可以通过npm下载,使用时百度就可以
declare var jQuery: (selector:string) => any

14.内置对象:

js中的内置对象,DOM和BOM中的对象在ts中均可支持。

15.项目中配置webpack:

1.新建一个项目文件,命名为:project,project文件夹下新建build、public、src文件夹。

2.在project文件夹下打开终端输入:npm init -y 初始化一个package.json文件。

3.继续在当前目录执行:tsc --init 将ts的配置文件初识化出来。

4.配置webpack.config.js文件如下:

// 7-1: 引入mini-css-extract-plugin插件:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 7-2: 解构定义配置插件:
const { DefinePlugin } = require('webpack')
// 7-3: 引入生成html文件的插件:
const htmlWebpackPlugin = require('html-webpack-plugin');
// 1.引入path包,webpack5中输出文件要求绝对路径,因此这需要引入path模块
const path = require('path')
module.exports = {
  // 2.打包入口文件
  entry: './src/main.js',
  // 3.打包输出文件
  output: { 
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  // 4.开发环境下,建议打开该选项
  // devtool: 'eval-source-map',
  // 5.配置打包环境:production生产环境、development开发环境,一般在package.json文件中配置命令即可
  mode: 'production',
  // 6.配置loader:
  module: {
    rules: [
      // A.将图片文件处理为base64位文件:npm i url-loader -S ,将url-loader先安装到本地,-S表示同时需要记录在package.json中
      // 此时去打包提示需要安装file-loader,原因是图片属于文件类型,因此还需要file-loader,继续npm i file-loader -S安装,后面其它loader安装方式一样,我就不重复了
      {
        test: /\.(jpg|svg|png|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 默认为true,即使用es6模块化;设为false,即使用commonJs模块语法
              esModule: false,
              outputPath: 'images',
              limit: 100*1024, // 小于100kb的图片转为base64
              name: '[hash].[ext][query]', // 自定义输出文件名
              // webpack5使用旧的assets loader(如file-loader/url-loader)
              // 可以加'javascript/auto'来解决图片无法生成images文件夹(目标文件夹)
              type: 'javascript/auto'
            }
          }
        ]
      },
      // B.处理js、react高级语法:
      {
        test: /\.(m?js|jsx)$/, // 处理js,和react文件
        exclude: /(node_modules|bower-components)/, // 忽略node_modules等文件
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env','@babel/preset-react'], // @babel/preset-env核心对js高级语法解析的工具,@babel/preset-react解析react语法
            plugins: ['@babel/plugin-proposal-object-rest-spread'] // 处理react的工具
          }
        }
      },
      // C.处理sass:
      {
        test: /\.scss$/,
        use: [
          // 以下是未使用MiniCssExtractPlugin插件配置:
          // "style-loader",
          // "css-loader",
          // "sass-loader"

          // 使用MiniCssExtractPlugin插件配置:
          MiniCssExtractPlugin.loader,
          "css-loader",
          "sass-loader"
        ]
      },
      // 处理ts文件:
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        include: [path.resolve('src')]
      }
    ]
  },
  // 7.插件:
  plugins: [
    // 7-1.从js文件抽离css代码到css文件:
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
      chunkFilename: '[id].css',
    }),
    // 7-2.定义配置,如不同环境的ip地址:DefinePlugin需要先从webpack中解构出来
    new DefinePlugin({
      // 这里定义的:SERVICE_URL可以在项目其他文件中使用
      'SERVICE_URL':JSON.stringify('https://blog.csdn.net/weixin_46758988')
    }),
    // 7-3:生成html文件:htmlWebpackPlugin默认可以什么也不配置,如果想要生成自定义html文件,那么就要对其配置:
    new htmlWebpackPlugin({
      title: 'my-app', // 用来设置网站标题
      filename: 'index.html', // 设置文件名,默认就为:index.html
      template: './src/index.html', // 默认html模板,在这个文件可以引入项目所依赖的其他文件
    })
  ],
  // 8.热替换:
  devServer: {
    // contentBase: path.join(__dirname,'dist'), // 启动文件位置,版本问题,开启会报bug
    compress: true, // 是否压缩代码
    port: 9000, // 端口
    // hot: false // 热替换,false表示热替换,true表示不热替换
  },
  // 9.webpack自身配置:
  // configureWebpack: {
  //   // 是否开启代码性能提示:有些文件大小超过webpack默认的配置大小会报警告
  //   performance: {
  //     hints: false, // 关闭,其值还可以是:warning表示警告:
  //     // 入口起点的最大体积
  //     maxEntrypointSize: 50000000,
  //     // 生成文件的最大体积
  //     maxAssetSize: 30000000,
  //     // 只给出 js 文件的性能提示
  //     assetFilter: function(assetFilename) {
  //       return assetFilename.endsWith('.js');
  //     }
  //   },
  // }
}

// module.exports = {
//   // 入口文件可以配置多个,此时可以打包出多个打包文件
//   entry: {
//     main: './src/main.js',
//     utils: './src/utils.js'
//   },
//   // 多个打包文件命名需要写成动态命名,name实际是入口配置中对象的key
//   output: { 
//     path: path.resolve(__dirname, 'dist'),
//     filename: '[name].bundle.js'
//   }
// }

5.下载相关依赖(注意:在这里上面配置中的css以及图片等用到到loader我这里没有做具体的下载,因为本地已经有下载,如果需要可以参考之前博文:https://blog.csdn.net/weixin_46758988/article/details/117451652 和 https://blog.csdn.net/weixin_46758988/article/details/117912182):

yarn add -D typescript
yarn add -D webpack webpack-cli
yarn add -D webpack-dev-server
yarn add -D html-webpack-plugin clean-webpack-plugin
yarn add -D ts-loader
yarn add -D cross-env

6.配置package.js中打包目录:

// 生产环境 
"build": "webpack --mode=production"
// 开发环境
"dev": "webpack --mode=development"
// 热更新启动项目
"server": "webpack-dev-server"

提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
笔者:苦海

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦海123

快乐每一天!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值