TypeScript 学习笔记

TS介绍

TypeScript 是一个开源的、渐进式包含类型的 JavaScript 超集,由微软创建并维护。创建它的目的是让开发者增强 JavaScript 的能力并使应用的规模扩展变得更容易。

它的主要功能之一是为 JavaScript 变量提供类型支持。在 JavaScript 中提供类型支持可以实现静态检查,从而更容易 地重构代码和寻找 bug。最后,TypeScript 会被编译为简单的JavaScript 代码。

  1. 要开始使用 TypeScript,我们需要用 npm 来安装它。

    npm install -g typescript
    
  2. 使用tsc对ts文件进行编译

    tsc xxx.ts
    

一些开发者还是更习惯使用普通的 JavaScript 语言,而不是 TypeScript 来进行开发。但是在 JavaScript 中使用一些类型和错误检测功能也是很不错的。

好消息是 TypeScript 提供了一个特殊的功能,允许我们在编译时对代码进行错误检测和类型 检测!要使用它的话,需要在计算机上全局安装 TypeScript。使用时,只需要在 JavaScript 文件 的第一行添加一句// @ts-check

TS类型

类型声明

通过类型声明可以指定TS中变量(参数、形参)的类型。指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错。

let 变量: 类型;

let 变量: 类型 =;

//圆括号外面的类型是用来设置返回值的类型
function fn(参数: 类型, 参数: 类型): 类型{
...
}
// 声明一个变量a,并且指定它的类型为 number
let a: number;

// a的类型设置为了number,在以后的使用过程中a只能是数字类型了
a = 19;


// 声明变量后直接赋值
let c: boolean = true;

// 也可以直接使用字面量进行类型声明

 
//JS 函数不考虑参数的类型和个数
// 有了ts就可以限制类型和个数了
// 指定返回值类型为number
function sum(a:number, b:number):number {
return a + b;
}

sum(1,2); //如果不是number类型或者个数不为2的话,会报错。

自动类型推断

TS拥有自动的类型判断机制,所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明。

let c: boolean = true;

// 上面的写法太啰嗦了,typescript有类型推断机制
// 它可以根据变量赋的值自动给变量设置一个类型

let d = true; //d 之后只能是布尔值了

基本类型

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

字面量类型

// 可以使用 | 来连接多个类型

let b: 'male' | 'female'
  
// 此时b可以赋值male或者female
b = 'male';

let c: boolean | string;
c = 'hello';
c = true;

any类型

一个变量设置类型为 any后相当于对该变量关闭了 ts 类型检测。

// any表示任意类型
let d: any;

声明变量如果不指定类型,则ts解析器会自定判断变量为 any 类型。

let e; //隐式声明了any类型
e = 'hello';
e = 123;

unknown 类型

unknown是类型安全的any,unkonwn类型的变量,不能直接赋值给其他变量。

因为any类型的变量可以赋值给任意变量。

类型断言

但是,如果想要把 unknown类型变量赋值给其他变量,可以使用类型断言(any类型也可以进行类型断言):

  1. 先进行 typeof 类型判断后,再赋值。

  2. 给unknown 或者 any变量类型断言,告诉解析器变量的实际类型。

    语法:
    	变量 as 类型
    or  <类型>变量
    
    let m: unknown;
    m = 'hello';
    // 类型断言
    let s: string;
    s = m as string; //写法一
    s = <string>m; //写法二
    
    let hello: any = '嘉琪';
    let p: string = <string>hello;
    

void 和 never

void用来表示为空,以函数为例,表示没有返回值或者返回null或者undefined。

function fn():void {
 return null;
}

never 表示永远不会返回结果。

object

一般设置对象的属性。

// Object 表示对象

let a: object; //用处不大

// 一般设置对象的属性
// {} 用来指定对象中可以包含哪些属性
// 语法:{属性名:属性类型}
let b: { name: string,age:Number };
b = { name: '嘉琪coder',age:22 };

// &表示同时
// j要有name和age两个属性
let j: { name: string } & { age: number };
j = {name:'jiaqi',age:12}

如果指定了对象属性类型,则之后需要完全按照指定的要求来写。


但如果在赋值的时候不想写age类型,可以在属性名后面加上?来表示属性是可选的。

let b: { name: string, age?: number };
b = { name: 'jiaqi' };

如果要求必须写某个属性,但其他的属性可写可不写。

[propName:string]:any 表示属性名为字符串类型,属性值为任意类型。 propName为随便写的变量名。

let c: {name: string,[propName:string]:any};
c = { name: 'jiaqi', a: 1, b: 'hello' }

设置函数的类型:

// 设置函数的结构类型声明
let d: (a: number, b: number) => number;
d = (n1:number, n2:number) =>{
 return n1+ n2;
}

array

类型[]或者Array<类型>来定义数组元素的类型。

// string[]表示字符串数组

let e: string[];

e = ['a', 'b'];

let f: number[];
let g: Array<number>;

tuple

元组是固定长度的数组。(ts新增)

// 元组是固定长度的数组

let h: [number, number];
h = [1, 2];//固定长度,只能是2个元素

//info是一个数组,它里面的元素是元组
let info: [number, string][];

info = [
 [1, '嘉琪'],
 [2, '小王'],
 [3,'小黑']
]

enum

枚举把所有可能的情况列举出来。(ts新增)

默认会给枚举的名称依次赋予0,1,2等这些值。不过可以给他们自行赋值。


// 枚举
enum Gender{
 Male,Female
}

console.log(Gender.Male); //0

let i: { name: string, gender: Gender }
i = {
 name: 'jiaqi',
 gender:Gender.Female,
}

enum Directions {
	up = 1,
	left = 4,
	right,
	down
}
//right的值为5,down的值为6

union

union的意思是并集。

let pid: string | number
pid = '22';
pid = 22;

type

可以用来给类型起别名,比如:

// k的值是1~5中的一个
let k: 1 | 2 | 3 | 4 | 5;
// 与此同时p的取值也和k一样,因此可以给类型起个别名

type myType = 1 | 2 | 3 | 4 | 5;
let p: myType;

函数定义

可以定义函数接收的参数类型,和返回值的类型。

// 接收2个number类型的变量,且返回值也是number类型
function addNum(x: number, y: number):number {
 return x + y;
}
function log(msg: string | number):void {
 console.log(msg);
}

interface

interface 不能用于定义原始值的类型,是用于定义对象类型(对象或者函数)。

  1. 普通对象使用interface:

    interface UserInterface {
     id: number,
     name:string
    }
    
    const user:UserInterface = {id:1,name:'jiaqi'}
    

    可以同时定义多个 同名的interface,最终的结果就是把他们合在了一起。

  2. 箭头函数使用interface:

    interface MathFunc {
      (x:number,y:number):number
    }
    
    const add: MathFunc = (x: number, y: number): number => x + y;
    const subtract: MathFunc = (x: number, y: number): number => x - y;
    
  3. 使用interface限制类的结构,只定义方法的结构,不考虑具体的实现。

    类实现接口使用的是 implements 关键字:

    interface PersonInterface {
      id: number,
      name: string,
      register():string,
    }
    
    class Person implements PersonInterface{
      id: number;
      name: string;
      constructor(id:number,name:string) {
        this.id = id;
        this.name = name;
      }
      register(): string {
          return `${this.name} is registered`
      }
    }
    

    如果说对象中的某个属性是可选的,则使用 ? 来定义。

    interface UserInterface {
      id: number,
      name: string,
      age?:32,
    }
    const user1:UserInterface = {id:1,name:'jiaqi'}
    

可以用 readobly修饰某个属性,表示只读,不可以修改其值。

泛型

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

泛型的英文是generics,我觉得它的作用是定义可复用的“组件”。<T>就相当于一个占位符,之后使用该“组件”的时候再给它一个确定的类型。

function getArray<T>(items: T[]): T[]{
  return items;
}

const numArray = getArray<number>([1, 2, 3, 4]);
const strArray = getArray<string>(['jaiqi', 'string']);

class MyClass<T>{
  name: T
  constructor(name: T) {
    this.name = name;
  }
}

const jiaqi = new MyClass<string>('jiaqi');

也可以同时指定多个泛型:

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

fn<number, string>(20, 'h');

可以让泛型继承自某个接口(或者类),就能限制泛型的范围。

interface MyInterface{
  length:number
}
// 要求传入的参数必须有length属性
function fn2<T extends MyInterface>(a:T):number {
  return a.length;
}

fn2({ length: 2 });
fn2('hello'); // 字符串自带length属性

TS编译选项

编译选项

自动编译文件

编译文件时,使用 -w 指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。

tsc xxx.ts -w

自动编译整个项目

在添加 tsconfig.json 配置文件后(空的也可以,只要创建这个文件),然后使用 tsc 命令即可完成对整个项目的编译。

使用 tsc --init 即可自动生成配置文件,而且带了compilerOptions的全部配置项和解释。

使用 tsc -w 即会监视所有的文件!

注意:一个要在添加 tsconfig.json文件后才能实现这个功能。

配置选项:

include

  • 定义希望被编译文件所在的目录

  • 默认为["**/*"]**代表任意文件夹,*代表任意文件)

  • 示例:

    "include":["src/**/*", "tests/**/*"]
    

上述示例中,所有src目录和tests目录下的文件都会被编译。

exclude

  • 定义需要排除在外的目录

  • 默认值:[“node_modules”, “bower_components”, “jspm_packages”]

  • 示例:

    • “exclude”: ``["./src/hello/**/*"]`

    • 上述示例中,src下hello目录下的文件都不会被编译

extends:

  • 定义被继承的配置文件

  • 示例:

    • “extends”: “./configs/base”

    • 上述示例中,当前配置文件中会自动包含config目录下base.json中的所有配置信息。

files

  • 指定被编译文件的列表,只有需要编译的文件少时才会用到

  • 示例:

    • “files”: [ "core.ts", "sys.ts", "types.ts", "scanner.ts", "parser.ts", "utilities.ts", "binder.ts", "checker.ts", "tsc.ts" ]

    • 列表中的文件都会被TS编译器所编译

compilerOptions

编译选项是配置文件中非常重要也比较复杂的配置选项。具体见下文。

compilerOptions

target

用来设置ts代码编译的目标版本。

可选值有: ES3(默认)、ES5、ES6/ES2015、ES2016、ES2017、ES2018、ES2019、ES2020、ES2021,ESNext。

比如下面瞎写了一点代码:

async function getStuff(url:string) {
 const res = await fetch(url);
 const result = await res.json();
 return result;
}

let url = 'https://api.wmdb.tv/api/v1/top?skip=0&limit=10';
console.log(getStuff(url));

当把target设置为ES2018时,编译后的js如下:

而当把target设置为ES3时(或者说不设置target,因为默认值是ES3):

module

设置编译后代码使用的模块化系统。可选值为:none, commonjs, amd, system, umd, es6, es2015, es2020, esnext。

"compilerOptions": {
    "module": "CommonJS"
}

lib

一般不用动!

指定代码运行时所包含的库(宿主环境)。可选值:ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost。

"compilerOptions": {
    "target": "ES6",
    "lib": ["ES6", "DOM"],
    "outDir": "dist",
    "outFile": "dist/aa.js"
}

outDir

编译后文件的所在目录。 默认情况下,编译后的js文件会和ts文件位于相同的目录,设置outDir后可以改变编译后文件的位置。

"compilerOptions": {  
     "outDir": "dist"  
}

设置后编译后的js文件将会生成到dist目录。

outFile

将所有的文件编译为一个js文件。默认会将所有的编写在全局作用域中的代码合并为一个js文件,需要将module指定为None、System或AMD则会将模块一起合并到文件之中。

"compilerOptions": {
   "target": "ES6"
}

allowJS

是否对JS文件进行编译,默认为false。

"compilerOptions": {
	"allowJS": false
}

checkJS

是否检查JS代码是否符合ts语法规范,默认值为false。

removeComments

编译后是否移除注释,默认为false。

"compilerOptions": {
	"removeComments": false
}

noEmit

是否不生成编译后的文件,默认为false。

"compilerOptions": {
	"noEmit": false
}

noEmitOnError

当有错误的时候就不生成编译文件,默认为false。

"compilerOptions": {
	"noEmitOnError": false
}

strict

所有严格检查的总开关,默认值为false。

alwaysStrict

用来设置编译后的js文件是否使用严格模式(即自动加上“use strict”语句),默认为false。

"compilerOptions": {
	"alwaysStrict": false
}

注意:当js文件有 import export等模块化语句时,就会自动进入到严格模式,ts编译器就不会多此一举写上'use strict'语句。

noImplicitAny

不允许隐式的any类型,默认为false。

function fn(a,b){
	return a+b;
}
// 如果不开启该选项 a和b就会默认设置为any类型

noImplicitThis

不允许不明确类型的this,默认为false。

strictNullChecks

严格的空值检查,默认为false。可以检查出有可能为Null的变量。

使用webpack打包TS

基本配置

下载相关依赖:

yarn add webpack webpack-cli typescript ts-loader -D

进行配置:

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',
  },
  
  // 指定要使用的loader
  module: {
    // 指定加载的规则
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        // 要排除的文件夹
        exclude: /node-modules/
      }
    ]
  }
}

tsconfig.json

//tsconfig.json
{
 "compilerOptions":{
 "module":"ES6",
 "target":"ES6",
 "strict":true
 }
}

package.json

{
 "devDependencies": {
	省略
 },
 "scripts": {
  "build":"webpack"
 }
}

最后在终端中输入 yarn build,即可开始进行打包。

进阶配置

html-webpack-plugin

可自动生成HTML文件,并自动引入js文件。

yarn add html-webpack-plugin -D

webpack.config.js


const path = require('path');

+ // 引入插件
+ const HtmlWebpackPlugin = require('html-webpack-plugin');

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: './src/index.ts',
  mode:'production',
  // 指定输出位置
  output: {
    // 指定打包后的目录
    path: path.resolve(__dirname, 'dist'),
    //打包后的文件名
    filename:'bundle.js',
  },
  
  // 指定要使用的loader
  module: {
    // 指定加载的规则
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        // 要排除的文件夹
        exclude: /node-modules/
      }
    ]
  },
+  // 插件配置
+  plugins: [
+    new HtmlWebpackPlugin(),
+  ]
}

还可以在该插件中进行一些配置:

plugins: [
 new HtmlWebpackPlugin({
 // title: '自定义title',
 template:'./src/template.html', //以某某文件为模板来生成html文件
 }),
]

webpack-dev-server

webpack开发服务器,可以监视代码更新,自动打开网页。

yarn add webpack-dev-server -D

package.json

"scripts":{
	"start":"webpack serve --open"
}

clean-webpack-plugin

每次编译前,自动清除dist目录下的文件。(如果不适用该插件将是新文件替代旧文件,可能存在部分旧文件没有被替代的情况)

yarn add clean-webpack-plugin -D

webpack.config.js

const path = require('path');

// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const { CleanWebpackPlugin } = require('clean-webpack-plugin');

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: './src/index.ts',
  mode: 'production',
  // 指定输出位置
  output: {
    // 指定打包后的目录
    path: path.resolve(__dirname, 'dist'),
    //打包后的文件名
    filename: 'bundle.js',
  },

  // 指定要使用的loader
  module: {
    // 指定加载的规则
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        // 要排除的文件夹
        exclude: /node-modules/
      }
    ]
  },
  // 插件配置
  plugins: [
    new HtmlWebpackPlugin({
      // title: '自定义title',
      template: './src/template.html', //以某某文件为模板来生成html文件
    }),
+    new CleanWebpackPlugin(),
  ]
}

resolve

extension:用来指明哪些文件可以作为模块引入

const path = require('path');

// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  // 指定入口文件
  entry: './src/index.ts',
  mode: 'production',
  // 指定输出位置
  output: {
    // 指定打包后的目录
    path: path.resolve(__dirname, 'dist'),
    //打包后的文件名
    filename: 'bundle.js',
  },

  // 指定要使用的loader
  module: {
    // 指定加载的规则
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        // 要排除的文件夹
        exclude: /node-modules/
      }
    ]
  },
  // 插件配置
  plugins: [
    new HtmlWebpackPlugin({
      // title: '自定义title',
      template: './src/template.html', //以某某文件为模板来生成html文件
    }),
    new CleanWebpackPlugin(),
  ],
  // 用来设置模块
+  resolve: {
+    extensions:['.ts','.js'], //以.ts和.js结尾的文件可以作为模块
+  }
}

配合babel

下载依赖

yarn add -D @babel/core @babel/preset-env babel-loader core-js

webpack.config.js

//...略
// 指定要使用的loader
module: {
// 指定加载的规则
rules: [
  {
    test: /\.ts$/,
    // use: ['babel-loader','ts-loader'], 但还可以写一些配置信息
    use: [
      {
        loader: 'babel-loader', // 指定加载器
        options: { //设置babel
          // 指定预定义环境
          presets: [
            ['@babel/preset-env', //指定环境的插件
            {
              // 指定需要兼容的浏览器版本
              targets: {
                "chrome": '88',
                "ie":'9',
              },               
              "corejs": "3", //指定corejs的版本
              // 使用coreJS的方式,usage表示按需加载
              "useBuiltIns":'usage'
            }]
          ]

        }
      }
      , 'ts-loader'],
    // 要排除的文件夹
    exclude: /node-modules/
  }
]
},
//...略

另外,也得告诉webpack打包后不要用箭头函数和const

output: {
 // 指定打包后的目录
 path: path.resolve(__dirname, 'dist'),
 //打包后的文件名
 filename: 'bundle.js',
+ environment: {
+	 arrowFunction:false,
+    const:false,
+ }
},

其他配置

配置scss

首先安装相应的依赖:

yarn add -D sass sass-loader css-loader style-loader

记住,loader的处理顺序是从右往左。

// 指定要使用的loader
module: {
  rules: [
    {
      test: /\.scss$/,
      use: [
        'style-loader',"css-loader",'sass-loader'
      ]
    }
  ]
},

接着需要在文件中引入样式:

import './style/index.scss';

配置post-css

首先安装相应的依赖:

yarn add -D postcss postcss-loader postcss-preset-env

注意postcss-loader的配置放在css-loader的位置之后(执行的时候先执行postcss-loader)

module: {
  rules: [
    {
      test: /\.scss$/,
      use: [
        'style-loader',
        "css-loader",
        {
          loader: 'postcss-loader',
          options: {
            postcssOptions: {
              plugins: [
                [
                  'postcss-preset-env',
                  {
                    browsers: 'last 2 versions'
                  }
                ],
              ]
            }
          }
        },
        'sass-loader'
      ]
    }
  ]
},

TS类

定义类

一个类由属性和方法组成。使用class关键字即可声明一个类:

class People{

}

使用 new 关键字可实例化该类,也即会返回一个新的对象:

const jiaqi = new People();

ts 定义属性:

类有两种属性,一是类属性和实例属性。实例属性是属于实例化对象的,而类属性则是属于类的。

实例属性直接写在类中,不需要在前面加上 const let var 等关键字,直接写!

类属性需要在声明前加上一个 static 关键字。

如果想要属性是只读的,可以在前面加上 readonly关键字。

// 使用class关键词定义一个类

// 直接定义的属性是实例属性,只有实例才能使用
// 使用static关键词定义的属性,只有类才能使用
class People {
  // 定义实例属性 实例化后的对象都会有这些属性
  name: string = '嘉琪';
  age: number = 23;
  // 使用static定义类属性(静态属性),即不需要实例化就可以使用的属性,属于类的属性
  static personName: string = 'jiaqicoder';
  // 只读属性
  readonly hobby: string = 'coding';
}

console.log(People.personName); //jiaqicoder
const per = new People();
per.name = 'tom';
console.log(new People()); //{ name: '嘉琪', age: 23, hobby: 'coding' }

当然,readonly很显然是ts的语法,js并不存在。

定义方法

直接写上方法的名字,不需要用 function 来声明。

如果方法名字前面加上了static则是类的方法,否则为实例的方法。

class People {
 // 定义实例方法
 sayHello() {
 console.log('hello 大家好');
 }
 // 定义类方法
 static eat() {
 console.log('I love food!');
 }
}

People.eat(); //I love food!
new People().sayHello(); //hello 大家好

构造函数

之前其实存在一个问题:实例化对象时无法向类传参。

定义的类,实例属性都是写得死死的。

class People{
	name = 'jiaqi';
}

new People().name; //jiaqi
new People().name; //jiaqi
new People().name; //jiaqi

所有new People()得到的name属性的值都是 jiaqi


我们希望创建一个通用的类,每次实例化可以创建不同的人。

这时,我们就可以使用构造函数(constructor),它会在实例化对象时自动被调用,而且还可以接收到传进来的参数

在类中或者constuctor中存在一个this,它指向new 类名()创建出的对象。通过this就可以像新创建的对象添加属性。(this指向新创建的对象)

class People{
	//constructor可以接收传进来的参数
	constructor(name,age){
		this.name = name; //将传进来的参数添加到this上
		this.age = age; //而this指向new出来的对象上
	}
}

const jiaqi = new People('嘉琪', 0);

此时,我们终于没有把 name 和 age 属性写死,新创建的对象是使用我们传进来的参数。

除了构造函数可以用到this,其他的函数中也可以用到this,比如

class People{
	//constructor可以接收传进来的参数
	constructor(name,age){
		this.name = name; //将传进来的参数添加到this上
		this.age = age; //而this指向new出来的对象上
	}
	eat(){
		console.log(this.name + '正在吃饭!')
	}
	
}

const jiaqi = new People('嘉琪', 0);
jiaqi.eat();

当然,如果是静态方法(类的方法)this肯定不是指向新创建的对象,而是指向类本身。(类方法只能类调用,所以指向类本身)

实例的对象是不能调用类方法

使用ts:

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

const jiaqi = new People('嘉琪', 22);
console.log(jiaqi);

继承

子类使用 extends 关键字继承父类,子类将拥有父类所有的方法和属性。子类如果存在和父类相同的方法,则子类中的方法会覆盖父类的方法

class Animal {
	eat(){
		console.log('eat something')
	}
}

class People extends Animal {

}
const jiaqi = new People();
jiaqi.eat(); // eat something

super

子类的方法可以通过super关键字引用它们的原型。这个关键字只能在子类中使用,而且仅限于类构造函数、实例方法和静态方法内部。在类构造函数中使用super可以调用父类构造函数。

 class Animal{
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log(`${this.name}正在吃`);
  }
}

class People extends Animal{
  gender: string;
  constructor(name:string,age:number,gender: string) {
    super(name, age); //super相当于父类,调用父类的构造函数
    // 此处写的constructor相当于重写了父类的constructor,因此先调用父类的构造函数
    this.gender = gender;
  }
  eat() {
    // super代表当前类的父类
    super.eat();//调用父类的eat方法

  }
}

const jiaqi = new People('jiaqi', 22, 'female');

抽象类

注意: 这是 ts 才有的语法!

以abstract开头的是抽象类,抽象类不能用于创建对象,其他的类可以继承它。

抽象类就是专门用于被继承的类。在抽象类中可以定义抽象方法,并且子类必须对其进行实现。

// 抽象类
abstract class Animal{
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log(`${this.name}正在吃`);
  }
  // 定义一个抽象方法 且没有方法的具体实现
  // 抽象方法必须定义在抽象类中,并且子类必须对其实现!
  abstract sayHello():void;
}

子类继承了抽象后,必须要对抽象方法进行具体实现。

class People extends Animal{
  gender: string;
  constructor(name:string,age:number,gender: string) {
    super(name, age); //super相当于父类,调用父类的构造函数
    // 此处写的constructor相当于重写了父类的constructor,因此先调用父类的构造函数
    this.gender = gender;
  }
  eat() {
    // super代表当前类的父类
    super.eat();//调用父类的eat方法
  }
  // 子类必须对其进行实现
  sayHello(){
      console.log('hello');
  }
}

修饰符

注意: 这是 ts 才有的语法!

修饰符除了static,readonly,还有 publicprotected, private

  • public:修饰的属性可以在任意位置(包括子类)访问和修改(默认值)

    constructor中显示声明public可以减少代码

    class C {
      constructor(public name: string, public age: number) {
      }
    }
    
    //就相当于
    
    class D {
      name: string;
      age: number;
    
      constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
      }
    }
    
  • protected:该属性可以在当前类和它的子类中访问和修改

  • private:私有属性只能在当前类内部进行访问和修改,通过添加方法让私有属性能够间接地被访问:

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

    除了在ts中使用这种方法,JS原生其实自带getset,也可以实现同样的效果:

    class Person {
      name: string;
      _age: number;
      constructor(name: string, age: number) {
        this.name = name;
        this._age = age;
      }
      get age() {
        return this._age;
      }
      set age(age: number) {
        if (age > 0) {
          this._age = age;
        }
      }
    }
    
    const jiaqi = new Person('jiaqi', 22);
    console.log(jiaqi.age); //22
    
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值