干货! 快速上手typescript的学习笔记 (对比JS的新特性,环境搭建,webpack配置,ts编译配置)

前提需要有js的基础

TypeScript

产生背景

TypeScript 起源于使用JavaScript开发的大型项目 。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。因此微软开发了TypeScript ,TypeScript能运行JavaScript 的代码,并扩展了JavaScript 的语法。相比于JavaScript ,它还增加了静态类型、类、模块、接口和类型注解方面的功能,更易于大项目的开发。

什么是TypeScript?

  • 以JavaScript为基础构建的语言
  • 一个JavaScript的超集
  • 可以在任何支持JavaScript的平台中执行
  • TypeScript扩展了JavaScript并添加了类型
  • TS不能被JS解析器直接执行 TS—编译—>JS

TypeScript增加了什么

  • 类型:抽象、枚举等
  • 支持ES的新特性
  • 添加ES不具备的新特性
  • 丰富的配置选项:比如TS可以被配置为编译成任何版本的JS
  • 强大的开发工具

TypeScript开发环境搭建

  1. 下载Node.js

  2. 安装node.js

  3. 使用npm全局安装typescript

    • 进入命令行

    • 输入:npm i -g typescript

在这里插入图片描述

  • 查看是否安装成功

在这里插入图片描述

  1. 创建一个ts文件

    在这里插入图片描述

  2. 使用tsc对ts文件进行编译

    在这里插入图片描述

  3. 自动生成JS文件

    在这里插入图片描述

基本类型

  • 类型声明

    • 如果类型声明和赋值分开

      let/const/var 变量名 : 类型

      在这里插入图片描述

    • 如果声明和赋值一起写,会自动进行类型检测

    在这里插入图片描述

  • 类型

    类型例子描述
    number1,-33,20任意数字
    string‘hello’, “hello”任意字符串
    booleantrue,false布尔值true或false
    字面量其本身限制变量的值就是该字面量的值
    any*任意类型
    unknown*类型安全的any
    void空值(undefined)没有值(或undefined)
    nerver没有值不能是任何值
    object{name: ‘abc’}任意的JS对象
    array[1,12,3]任意JS数组
    tuple[4,5]元组,TS新增类型,固定长度数组
    enumenum{A,B}枚举,TS中新增类型
    • 字面量

      let a: 10;
      a = 10;
      
      let b: "male" | "female"; // 赋值为"male" |或"female"
      b = "male";
      b = "female";
      b = "hello" // 报错 ERROR!
      
    • any

      表示是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测

      let a = any;  // 显式声明any
      let a; //隐式声明any
      // 赋值
      a = 10; // 正常
      a = 'hello'; // 正常
      a = true;  // 正常
      
    • unknown

      类型安全的any,不能直接给其他类型的变量

      let e: unknown;
      e = 10;
      e = 'hello';
      e = true;
      

      看似和any一样,其实不然。

      any举例:

      let s:string;
      let d;  // any
      s = d; // 不报错
      

      unknown举例:

      let e: unknown;
      let s: string;
      s = e; // 报错
      
      // 如果非要赋值
      // if语句
      if(typeof e === 'string') {
        s = e;
      }
      // 类型断言,告诉编译器e是字符串
      s = e as string;
      // 另外一种写法
      s = <string>e;
      
    • void

      主要作为返回值,用来表示空,以函数为例,就表示没有返回值的函数。

      function fn(): void {
        // ...
      }
      
    • never

      表示永远不会返回结果

      function fn2(): never{
        // ...
      }
      // 有一些函数永远不会返回结果
      function showError(): never{
        throw new Error('报错了!');
      }
      
    • object

      object表示一个js对象

      // 一般不使用这种声明
      let a: object;
      a = {};
      a = function () {
      
      }
      // 一般使用下面的写法,要求格式和类型
      let b: {name: string};
      b = {name: '孙悟空'}
      // 在属性名后面加上? 表示属性是可选的
      let c: {name: string, age?: number};
      c = {name: '孙悟空'}; // 不报错
      
      // 但是不能把需要的所有属性都写一边,所以就用下面的语法
      // [propName: string]: any便是任意类型的属性
      let d: {name: string, [propName: string]: any};
      c = {name: '猪八戒', age: 1, gender: '男'};
      
      // 声明function结构的类型声明
      // (形参: 类型, 形参: 类型) => 返回值
      let e: (a: number, b: number) => number;
      d = function(n1: number, n2: number): number {
        return 10;
      }
      
    • array

      // 声明1:类型[]
      let arr1: string[];
      arr1 = ['a', 'b', 'c'];
      // 声明2:Array<类型>
      let arr2: Array<number>;
      arr2 = [1, 2, 3];
      
    • tuple

      元组,固定长度的数组

      // 简单的声明
      let arr3: [string, string];
      arr3 = ['a', 'b'];
      arr3 = ['a', 'b', 'c']; //报错
      
    • enum

      枚举,把所有可能的类型列出来

      enum Gender{
        Male = 0;
        Female = 1;
      }
      
      let i: {name: string, gender: Gender};
      i = {
        name: '孙悟空',
        gender: Gender.Male; // 0
      }
      
      console.log(i.gender === Gender.Male);
      
    • | 和 &

      let a: number | string
      let b: {name: string} & {age: number};
      b = {name: '孙悟空', age: 18};
      
    • 类型的别名

      // 引例:
      let k: 1 | 2 | 3 | 4;
      let l: 1 | 2 | 3 | 4;
      // 麻烦
      // 解决
      type myType =  1 | 2 | 3 | 4;
      let k: myType;
      let j: myType;
      

编译文件配置

我们写一行代码手动敲tsc命令编译一次是很耗时的,相当麻烦,也无法自动编译,而且无法编译所有的ts文件。

所以目录下增加tsconfig.json配置文件用来编译所有的ts文件。

在这里插入图片描述

这时就算没有写内容,打开这个目录,执行tsc,它会自动编译这个目录下所有的ts文件成js文件,下面介绍各配置项

在这里插入图片描述

tsconfig.json常用的各配置项

以下列出常用的tsconfig.json配置

{
  /*
  tsconfig.json ts编译器的配置文件
  */
  /*
  "include" 用来指定哪些ts文件需要被编译
  ->  路径: ** 表示任意目录  * 表示任意文件
  */
  "include": [
    "./src/**/*"
  ],
  /*
  "exclude" 用来指定哪些ts文件不希望被编译,比如node_module下的ts文件
  默认值: ["node_modules", "bower_components", "jspm_packages"]
  */
  "exclude": [
    "./src/abc/*"
  ],
  /*
  "extends" 定义被继承的配置文件
  下面会让此配置文件中自动包含config下base.json中的所有配置信息
  */
  "extends": "./config/base",
  /*
  "files" 指定被编译文件的列表,只有需要编译的文件少时才会用到
  列表中的文件都会被TS编译器所编译
  */
  "files": [
    "core.ts",
    "sys.ts"
  ],
  /*
  "compilerOptions" 编译选项时配置文件中非常重要也比较复杂的配置选型
  在compilerOptions中包含多个子选项,用来完成对编译的配置
  */
  "compilerOptions": {
    // target
    // 设置ts代码编译成JS的目标版本
    // 可选值:ES3(默认),ES5,ES6/ES2015,ES7/ES2016,ES2017,ES2018,ES2019,ES2020,ES2021
    "target": "ES2015",
    // lib
    // 指定代码运行时所包含的库(宿主环境) 一般情况下不改动
    // 可选值:ES5,ES6/ES2015,ES7/ES2016,ES2017,ES2018,ES2019,ES2020,ESNext,DOM...
    "lib": ["ES2015", "DOM"],
    // module
    // 设置编译后代码使用的模块化系统,指定模块化的规范
    // 可选值:CommonJS,AMD,UMD,ES2020.ESNext,None,System
    "module": "CommonJS",
    // outDir
    // 用来指定编译后js文件所在的目录
    "outDir": "./distJS",
    // outFile
    // 将代码合并为一个文件,全局作用域中的代码会合并
    // 如果要合并几个模块,要用AMD或者System
    "outFile": "./distJS/app.js",
    // allowJs
    // 是否对js文件进行搬移,默认是false
    "allowJs": false,
    // checkJs
    // 是否检查js代码是否符合语法规范,默认是false
    "checkJs": false,
    // strict 建议开启
    // 所有严格检查的开关 默认false
    "strict": false,
    // removeComments
    // 是否移除注释,默认false
    "removeComments": false,
    // noEmit 
    // 不生成编译后的js文件
    "noEmit": false,
    // noEmitOnError
    // 当有错误时,它不会生成编译后的js文件,默认false
    "noEmitOnError": true,
    // alwaysStrict
    // 用来设置编译后的文件是否使用严格模式,默认false
    "alwaysStrict": false,
    // noImplicitAny
    // 检查隐式any,true为不允许隐式any 默认false
    "noImplicitAny": true,
    // noImplicitThis
    // 检查隐式this,来源不明确的this 默认false
    "noImplicitThis": true,
    // strictNullChecks
    // 严格的检查空值null隐患 默认值false
    "strictNullChecks": false
  }
}

使用webpack打包ts代码

安装webpack

npm i -D webpack webpack-cli typescript ts-loader

配置webpack

只讲ts有关的,打包css,js图片等loader配置可以看我另外一个文章

webpack.config.js

// 引入一个包
const path = require('path');
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: "./src/index.ts",

  // 指定打包文件所在的目录
  output: {
    // 指定打包文件的目录
    path: path.resolve(__dirname,'dist'),
    // 打包后文件的名字
    filename: "bundle.js"
  },

  // 指定wegpack打包时要使用的模块
  module: {
    // 指定要加载的规则
    rules: [
      {
        // test指定的时规则生效的文件
        test: /\.ts$/,
        // 要使用的loader
        use: 'ts-loader',
        // 要排除的文件
        exclude: /node_modules/
      }
    ]
  }
}

配置ts编译规则

tsconfig.json

{
  "compilerOptions": {
    "module": "ES2015",
    "target": "ES2015",
    "strict": true
  }
}

增加package.json中script的命令

"build": "webpack"

这样最基本的配置已经完成了

试编译ts代码

编写index.ts

function sum(a: number, b: number): number{
  return a + b;
}

console.log(sum(1, 10));

npm run build

在这里插入图片描述

报的错是没指定环境是生产模式还是开发模式

想要解决这个错误就在webpack配置文件里增加mode配置,指定模式即可。

安装html-webpack-plugin

npm i -D html-webpack-plugin

让webpack自动生成html

配置插件

// 引入一个包
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: "./src/index.ts",

  // 指定打包文件所在的目录
  output: {
    //...
  },

  // 指定wegpack打包时要使用的模块
  module: {
    //...
  },
  "mode":"development",
  // 配置webpack插件
  plugins: [
    new HtmlWebpackPlugin(),
  ]
}

这样它会在build以后自动生成html并且自动引入ts编译后的js文件

安装webpack-dev-server

npm i -D webpack-dev-server

可以让我们的项目跟浏览器联系在一起,代码一修改后自动刷新,展现在浏览器中

安装完后增加script命令

"strat": "webpack serve --open chrome.exe"

安装 clean-webpack-plugin

npm i -D clean-webpack-plugin

可以在每次新编译后,将dist文件夹里面的文件清空,把新的文件加进去,让dist文件夹里面都是最新编译的文件。

配置webpack,告诉它引用的模块

因为如果你再创建一个ts,在index.ts中引入了新建的ts,webpack并不知道新建的ts可以作为模块引入,它会报错。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

增加webpack配置

  // 用来设置引入模块
  resolve: {
    extensions: ['.js', '.ts']
  }

在这里插入图片描述

安装babel.js以提高兼容性

因为现在编译出来的js为ES6的语法,对于像IE的兼容性很不好,所以现在引入babel.js将我们的语法编译成像ES5提高兼容性。

安装

npm i -D @babel/core @babel/preset-env babel-loader core-js

preset-env 是babel预置不同的环境使得兼容不同的浏览器

core-js 模拟js运行环境,可以让老浏览器很好的运行js

配置webpack

// 引入一个包
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const { CleanPlugin } = require('webpack');

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: "./src/index.ts",

  // 指定打包文件所在的目录
  output: {
    // 指定打包文件的目录
    path: path.resolve(__dirname,'dist'),
    // 打包后文件的名字
    filename: "bundle.js"
  },

  // 指定wegpack打包时要使用的模块
  module: {
    // 指定要加载的规则
    rules: [
      {
        // test指定的时规则生效的文件
        test: /\.ts$/,
        // 要使用的loader
        use: [
          // 配置babel
          {
            // 指定加载器
            loader: "babel-loader",
            // 设置babel
            options: {
              // 设置预定义的环境
              presets: [
                [
                  // 指定环境的插件
                  "@babel/preset-env",
                  // 配置信息
                  {
                    targets: {
                      // 指定兼容浏览器的版本
                      "chrome": "58",
                      "ie": "11",
                    },
                    // 指定corejs的版本
                    "corejs":"3",
                    // 使用corejs的方式"usage表示按需加载
                    "useBuiltIns":"usage"
                  }
                ]
              ]
            }
          },
          'ts-loader'
        ],
        // 要排除的文件
        exclude: /node_modules/
      }
    ]
  },
  "mode":"development",
  // 配置webpack插件
  plugins: [
    new HtmlWebpackPlugin({
      //title:"这是一个自定义的title"
      template: "./src/index.html"
    }),
    new CleanPlugin()
  ],
  // 用来设置引入模块
  resolve: {
    extensions: ['.js', '.ts']
  }
}

index.ts:

const abc = {name:'abc', age: 12};
console.log(abc);

不使用babel进行编译,他编译后的变量声明是const

在这里插入图片描述

使用babel进行编译,编译后变成了var声明变量

在这里插入图片描述

像ie11没有Promise,如果我们使用了Promise,corejs会产生效果,使用自己实现的promise加在编译后的js中

index.ts

const abc = {name:'abc', age: 12};
console.log(abc);
console.log(Promise);

进行编译,corejs会加入很多自己实现的东西

在这里插入图片描述

但是这样在ie中还是会出错,因为webpack使用了箭头函数,我们可以增加配置项让webpack不使用箭头函数

output:{
   // ...
   // 告诉webpack不使用箭头函数
   environment: {
     arrowFunction: false
   }
}

这样ie就不会报错了

面向对象

以下话摘抄与尚硅谷的视频教程

面向对象是程序中一个非常重要的思想,它被很多同学理解成了一个比较难,比较深奥的问题,其实不然。面向对象很简单那,简而言之就是程序之中所有的操作都需要通过对象来完成。

  • 举例来说
    • 操作浏览器需要用到window对象
    • 操作网页要用到document对象
    • 操作控制台要用到console对象

一切操作都要通过对象,也就是所谓的面向对象,那么对象到底是什么呢?这就要先说到程序是社么,计算机程序的本质就是对现实事务的抽象,抽象的反义词是具体,比如:照片是一个具体的人的抽象,汽车模型是对具体汽车的抽象等等。程序也是对事务的抽象,在程序中我们可以表示一个人,一条狗,一把枪,一颗子弹等等所有的事物,一个事物到了程序中就变成了一个对象。

在程序中所有的对象都被分成了两个部分,数据和功能,以人为例,人的姓名、性别、年龄、身高等属于数据,人可以说话吃饭睡觉,这些属于人的功能,数据在对象中被称为属性,而功能就被称为方法,所以简而言之,在程序中一切皆是对象。

类(class)

要想面向对象,操作对象,首先要拥有对象,那么下一个问题就是如果创建对象,要创建对象,必须要先定义类,所谓的类可以理解为对象的模型,程序中可以根据类创建指定类型的对象,举例来说,可以通过Person类创建人的对象,Dog类创建狗的对象。

定义类
class 类名 {
  属性名 : 类型;
  constructor(参数: 类型) {
    this.属性名 = 参数;
  }
  方法名() {
     // ...
  }
}
实例及各部分解释
class Dog{
  // 通过直接定义的属性是实例属性,需要通过对象的实例去访问
  name: string;
  age: number;
  // 通过static开头的属性是静态属性(类属性)可以通过类直接访问
  static footNum: number = 4;
  // readonly 把属性变成只读的属性,无法更改
  readonly color: string = 'white';

 // 每次定义一个对象,构造函数规定了定义对象的规则,每次调用new Dog构造函数就会每次执行。
 // 如果有参构造,那么创建对象的时候,必须传入构造对象规定的参数
 // 构造函数会根据传入的参数构造相应的对象
  constructor(name: string, age: number) {
    // 在构造函数中,你创建了新对象,this则会指向新对象
    this.name = name;
    this.age = age;
  }
 // 如果无参构造,那么创建对象的时候,不用传参直接创建即可,但是无参构造会创建name和age相同的对象。

  eat():void {
    console.log('吃肉');
  }

  sound():void {
    console.log('汪汪');
  }
}

// 定义对象
const dogOne = new Dog('小毛', 12);// 创建了name为小毛,age为12的对象
const dogTwo = new Dog('小黑', 8); // 创建了name为小黑,age为8的对象
console.log(dogOne.name) // '小毛'
console.log(Dog.footNum) // 4
dogOne.eat(); // '吃肉'
继承

Dog类

class Dog{
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  eat():void {
    console.log('吃肉');
  }

  sound():void {
    console.log('汪汪');
  }
}

Cat类

class Cat{
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  eat():void {
    console.log('吃鱼');
  }

  sound():void {
    console.log('喵喵喵');
  }
}

我们发现Cat类和Dog类都有name和age属性,都有eat和sound方法。这样代码重复性很高,我们如果要定义别的动物类,还要再写一遍相同的内容。所以就引出了继承这个概念:

Cat类和Dog类它们其实都算动物这个大类,动物都有自己的昵称和年龄,都会有爱吃的东西和自己的叫声。所以Cat类和Dog类相当于继承了Animal类。此时Animal类被称为父类,Cat类和Dog类被称为子类。继承后子类将会拥有父类所有的方法和属性。

举例:

Animal类

class Animal{
  name: string;
  age: number;
  food: string;
  sound: string;

  constructor(name: string, age: number, food: string, sound: string) {
    this.name = name;
    this.age = age;
    this.food = food;
    this.sound = sound;
  }

  eat():void {
    console.log('吃:' + food);
  }

  showSound():void {
    console.log(sound);
  }
}

Dog类

class Dog extends Animal{

}

Cat类:

class Cat extends Animal{

}

创建对象:

const DogOne = new Dog('小黑', 8, '肉', '汪汪汪~');
const CatOne = new Cat('小喵', 12, '鱼', '喵喵喵~');
console.log(DogOne);
console.log(CatOne);
DogOne.eat();
DogOne.showSound();
CatOne.eat();
CatOne.showSound();

在这里插入图片描述

通过继承可以将多个类中共有的代码写在一个父类里,这样只需要写一次即可让所有的子类都同时拥有父类中的属性和方法,如果希望在子类中添加一些父类没有的属性或方法直接加在子类中即可。

如果再子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的相同的方法。

比如:

class Dog extends Animal{
  eatShit():void{
    console.log('吃shi');
  }
}
class Cat extends Animal{
  kneading():void{
    console.log('踩奶');
  }
}
super关键字

在类的方法中,super就表示当前类的父类

Animal类:

class Animal{
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  eat():void {
    console.log('吃饭');
  }

}

Dog类:

class Dog extends Animal{
  eat():void {
    super.eat(); // 吃饭
  }
}

上述例子用得不多

如果我们要往子类中添加了新的不同于父类的属性,那么我们需要调用构造函数去创建对象

如果子类中写了构造函数,在子类构造函数中,必须对父类的构造函数进行调用

class Dog extends Animal{
  gender: string;
  
  constructor(gender: string, name: string, age: number) {
    // 不加super是会报错的,如果调用了super需要传入父类构造函数定义的形参
    super(name,age);
    this.gender = gender;
  }
}
抽象类

如果像我们之前写的Animal类

class Animal{
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  eat():void {
    console.log('吃饭');
  }

}

它是可以去创建对象的

const animalOne = new Animal('小青', 12);

但是这样是有问题的,创建的范围过大,我们无法分别它是什么动物。

我们开始创建Animal类是希望它去定义一些动物共同的属性和方法,让它的不同的分支动物去继承它,不再重复的编写代码。但是像上述代码,是有问题的。所以我们希望Animal类**只去定义共同的属性和方法,没法创建对象,而且我们希望定义的方法,没有具体的实现内容,只定义了我这个大类,有这种方法的概念,具体如何实现,是各分支子类的任务,我只负责定义概念。**这就引出了抽象类的概念。

抽象类就是,当你不希望这个类创建对象的时候,你可以将它定义为抽象类,抽象类里面可以定义抽象方法,抽象方法没有实现内容,且只能定义在抽象类里。

定义抽象类:

定义一个抽象方法:

  1. 使用abstract关键字开头

  2. 没有方法体

  3. 抽象方法只能定义在抽象类中

  4. 子类必须对抽象方法进行重写

定义Animal类:

abstract class Animal{
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  abstract showSound():void; // 没有方法体
}

定义Dog类

class Dog extends Animal{
  // 必须重写
  showSound() {
    console.log('汪汪汪~')
  }
}

定义Cat类:

class Cat extends Animal{
  // 必须重写
  showSound() {
    console.log('喵喵喵~')
  }
}
接口

接口是一种规范的定义,它定义了行为和动作规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批所需要遵守的规范,它不在乎类内部的状态,只在乎你是否提供了它规定的规则,比如应该含有的方法,应该含有的属性。

像我们电脑,鼠标的接口是usb的,现在手机数据线的接口是type-C的,它规定了这类规范的产物,只能用于对应插口的使用,而不能混着插。你必须是含有这类接口定义规范的产物才可以使用对应口。

之前声明类型的时候,我们声明一个对象的类型可以写为

type myType = {
  name: string;
  age: number;
}

const obj: myType = {
  name: 'sss',
  age: 12
}

定义接口用interface关键字

interface myInterface{
  name: string;
  age: number;
}

const obj: myInterface = {
  name: 'sss',
  age: 111
}

接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法

同时接口也可以当成类型声明去使用(↑上述例子)

在我们ts中,接口可以重复定义,如果是同名的接口,则是几个同名接口的并集

interface myInterface{
  name: string;
  age: number;
}
interface myInterface{
  gender: string;
}
const obj: myInterface = {
  name: 'sss',
  age: 111,
  gender: '男';
}

接口限制类的特点:

  1. 接口在定义类的时候去限制类结构

  2. 接口中的所有属性都不能有实际的值

  3. 接口只定义对象的结构,而不考虑实际值

  4. 在接口中所有的方法都是抽象方法

定义一个接口:

interface myInter{
  name: string;
  sayHello(): void;
}

定义一个类,实现接口(就是满足接口的要求)

class MyClass implements myInter{
  name: string;
  
  constructor(name: string){
    this.name = name;
  }
  
  sayHello() {
    console.log('hello~');
  }
}

接口规范类结构有什么好处,比如一款FPS游戏,枪都可以开,雷都可以扔了炸,头饰、背饰、等等等,但是它们都需要实现一个特定的规范和结构后它们才能成为各种道具。

抽象类中可以有抽象方法和普通方法,接口中只能有抽象方法。抽象类使用extends,接口使用implements。

属性的封装

JavaScript中,创建出来的对象,它的属性的值是不安全的,像Java中,有private public final 等修饰符,set get 方法, JavaScript是没有的。它就导致:

class Person {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

const personOne = new Person('abc', 12);
// 你还可以修改它的属性值
personOne.name = 'aaa';
personOne.age = -12;
console.log(personOne);

在这里插入图片描述

没有一个人的岁数是负值。这就是JavaScript遗留下的一个坑。

typescript中,新增了类似Java中的变量修饰符

  1. public 修饰的属性可以在任意位置访问(修改) 默认值
  2. private 私有属性,私有属性只能在类内部进行访问(修改)
    1. 如果要访问私有属性,可以添加get set 方法间接的访问私有属性
  3. protected 保护属性,在类的里面,子类里可以访问,在类的外部无法访问。
  4. readOnly 只读属性,将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化

修改上述例子:

class Person {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  
  getName() {
    return this.name;
  }
  setName(value: string) {
    this.name = value;
  }
}

const personOne = new Person('abc', 12);
console.log(personOne.getName());
personOne.setName('ccc');
console.log(personOne);

这样做的好处就是,虽然还是能修改,但是修改权不在调用他的人,而在定义它的人,它可以设置有没有get set 方法,如果要修改则可以定义修改的规则。

TS中设置getter方法的方式(跟之前的直接赋值的观感是一样的)

get name() {
  return this.name;
}
set name(value: string) {
  this.name = name;
}

// 调用:
const personOne = new Person('abc', 12);
personOne.name = 'aaa';
console.log(personOne.name) // 'aaa'
泛型

在定义函数或是类时,如果遇到类型不明确就可以使用泛型

泛型的定义

function fn<T>(a: T): T{
  return a;
}

如果使用any:

function fn(a: any): any{
  return a;
}

首先泛型这样定义,函数的返回值的类型和a的类型时一样的。这样就比下面这样用any的方法好。使用any定义我们并不知道a的类型和函数的返回类型是否一致。

泛型也可以定义多个

function fn2<T, K>(a: T, b: K): T{
  console.log(b);
  return a;
}

建议最好在使用的时候,将泛型的类型限制一下

fn2<number, string>(123, 'abc');

我们如果要限制一下泛型的范围,我们可以用接口限制一下,这表示泛型T必须是Inter的实现类(子类)

interface Inter{
  length: number;
}
function fn3<T extends Inter>(a: T): number{
  return a.length;
}

这下我们传的时候,至少包含length这个属性

fn3('123') // 字符串有length属性
fn3( { name: 'asd'} ); //没有length属性
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值