TypeScript快速入门

第一章 基本数据及环境配置

1、基本类型

  • 类型声明

    • 类型声明是TS非常重要的一个特点
    • 通过类型声明可以指定TS中变量(参数、形参)的类型
    • 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错。
    • 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
  • 语法:

    let 变量:类型;
    let 变量:类型=值;
    function fn(参数:类型,参数:类型):类型{}
    
  • 自动类型判断

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

2、类型

image-20221101152735802

ts中的类型声明类似于python中的py文件func和pyi文件(stub)存根类型检查

// string & number & boolean & 联合类型(字面量)
let str1: string;
const str2: string = '10';
const str3: string = '11';

let a: number;
let b: number = 0;
const c: number = 10;
const d: number = 11.11;

let bo1: boolean;
let bo2: boolean = true;
const bo3: boolean = false;

let n: number | string;
const n2: number | string = '';
let n3: 'male' | 'female'; n3 = 'female';


////////////////////////////////////////////////////////////////////////////////////////////////
// any 直接使用的是js的类型,没有使用ts的类型检查
// unknown实际上就是一个类型安全的any,unknown类型的变量,不能直接赋值给其他变量: other != unknown;

let an: any; an = 10; an = 'an'; an = {}; an = []; an = true;
let unkn1: unknown; unkn1 = 10; unkn1 = 'unkn1'; unkn1 = {}; unkn1 = []; unkn1 = true;


// any 与 unknown 的使用区别
let str: string;
let an1: any; an1 = 10; str = an1;
let un1: unknown; un1 = 'string'; str = un1; 	//错误示范:不能将类型“unknown”分配给类型“string”。


// 使用确定解决unknown
// 断言使用
str = un1 as string;        
str = <string>un1;
// 判断类型
if (typeof un1 === 'string') str = un1;     


////////////////////////////////////////////////////////////////////////////////////////////////
// void 没有返回值或返回值为undefined,多用于函数中
// never 没有返回,注意连返回都没有,绝对的没有。连空值都没有。(少),一般用户throw error
function func1(): void { }
function func2(): void { return }
function func3(): void { return undefined }


function func4(): never { }					  //错误示范:返回“never”的函数不能具有可访问的终结点。
function func5(): never {
    throw new Error("this function is never type raise(throw) error!");
}


////////////////////////////////////////////////////////////////////////////////////////////////
// object => [],{},function,()=>{}...
// Array 数组 | []
// 对象 {}
// 函数 function | ()=>{}
// 枚举 enum 
// 类型别名 type


// object (少)
let o1: object;
let o2: object = [];
let o3: object = {};
let o4: object = function () { };
let o5: object = () => { };

// Array 数组
let arr: Array<number>;
let arr2: string[];
let arr3: number[];
let arr4: number[] | string[];
let arr5: Array<number | string>;
let arr6: Array<any>;             
let arr7: string['11' | '22'];    //arr7 type is string
let arr8: Array<boolean>;


arr4 = [10, 20, '']     				//错误示范: string[] or number[]
arr5 = [10, 20, '']     				// [string or number]
arr7 = ['11']       					//错误示范: arr7:string
arr8 = [true, false, false]


// {} 对象
let dic1: {};
let dic2: { name: string, age: string };
let dic3: { name: string, age?: string };                             // key?:keytype,可选参数
let dic4: { name: string, age: number, [propName: string]: any }      // {name,age,...}
dic1 = { name: 'alex', age: 20 }
dic2 = { name: 'alex', age: 20 }				//错误示范:不能将类型“number”分配给类型“string”
dic3 = { name: 'alex' }
dic4 = { name: 'alex', age: 20, sex: 'male' }



// function
let f1 = function () { };
let f2 = function (a: number, b: number): number { return a + b; };
let f3 = function (a: number, b: number, c?: number): number { return c ? a + b + c : a + b; };
let f4 = function (...props: any[]): any { }
let f5 = () => { };


f1 = function (a: string) { };		//错误示范:不能将类型“(a: string) => void”分配给类型“() => void”
f2 = function (num1: string, num2) { return num1 + num2; }  	//错误示范: 不能将类型“number”分配给类型“string”。
f3 = function (num1: number, num2: number) { return num1 + num2 }
f4 = function (a: string, b: number, c: number, d: {}, e: []) { return }
f5 = (name: string) => { }		//错误示范: 不能将类型“(name: string) => void”分配给类型“() => void”。


// tuple 确定长度的数组
let tup1: [string, number];
tup1 = ['1', 1, 1] 				//错误示范: 源具有 3 个元素,但目标仅允许 2 个。


// enum 枚举类型
let dic: {
    name: string,
    age: number,
    sex: 'male' | 'female',
}
dic = { name: 'alex', age: 20, sex: 'male' }

if (dic.sex === 'male') console.log(dic.name)



enum Gender {
    male = 0,
    female = 1,
}
let dic_enum: {
    name: string,
    age: number,
    sex: Gender,
};
dic_enum = { name: 'alex', age: 20, sex: Gender.male }
if (dic_enum.sex === Gender.male) console.log(dic_enum.name)


// 数据类型筛选 & : type1 & type2

let ty1: string & number;
let ty2: { name: string, age: number } & { gender: Gender };        // 多个对象,在一定条件下才选择可合并

let dic11 = { name: 'alex', age: 20 };
let dic12 = { gender: 'male' };
let dic13 = { gender: Gender.female };

ty2 = { ...dic11, ...dic12 }		//错误示范: 不能将类型“string”分配给类型“Gender”。
ty2 = { ...dic11, ...dic13 }



// 类型别名 type somemane = **type
let tm1: 1 | 2 | 3 | 4 | 5 | 6;
tm1 = 7								//错误示范: 不能将类型“7”分配给类型“2 | 3 | 1 | 4 | 5 | 6”。
tm1 = 1

type choice_number_range_one_to_six = 1 | 2 | 3 | 4 | 5 | 6;
let tm2: choice_number_range_one_to_six;
tm2 = 7								//错误示范: 不能将类型“7”分配给类型“2 | 3 | 1 | 4 | 5 | 6”。
tm2 = 1


3. TS编译选项

tsc -w 监视模式

1.tsconfig.json属性:

  • 使用"files"属性

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

    {
        "files": [
            "core.ts",
            "sys.ts",
            "types.ts",
        ]
    }
    //"files"指定一个包含相对或绝对文件路径的列表。 
    
  • 使用"include""exclude"属性

    {
        "include": [
            "src/**/*"
        ],
        "exclude": [
            "node_modules",
            "**/*.spec.ts"
        ]
    }
    

    "include""exclude"属性指定一个文件glob匹配模式列表。 支持的glob通配符有:

    • * 匹配0或多个字符(不包括目录分隔符)
    • ? 匹配一个任意字符(不包括目录分隔符)
    • **/ 递归匹配任意子目录
    • (默认.ts.tsx,和.d.ts, 如果 allowJs设置能true还包含.js.jsx)
  • extands

    • 定义被继承的配置文件

    • 示例:

      " extends": "./configs/base""
      

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

  • compilerOptions

    • target & module

      target 用来指定ts被编译为的ES的版本, module 指定要使用的模块化的规范.

      // target  用来指定ts被编译为的ES的版本
          'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 
          'es2019', 'es2020', 'es2021', 'es2022', 'esnext'.
      // module 指定要使用的模块化的规范
          'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 
          'es2020', 'es2022', 'esnext', 'node16', 'nodenext'.
      {
      	"compilerOptions":{
              "target":"es2015",
       		"moudle":"es2015"
          }    
      }
      
    • lib

      lib 用来指定项目中要使用的库, 一般使用默认的即可。(可按需引入)

      // lib 用来指定项目中要使用的库,一般使用默认的即可。
      {
      	"compilerOptions":{
              "lib":[
                  'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 
                  'es2021', 'es2022', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 
                  'webworker.iterable', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 
                  'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 
                  'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 
                  'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 
                  'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 
                  'es2019.symbol', 'es2020.bigint', 'es2020.date', 'es2020.promise', 'es2020.sharedmemory', 'es2020.string', 
                  'es2020.symbol.wellknown', 'es2020.intl', 'es2020.number', 'es2021.promise', 'es2021.string', 
                  'es2021.weakref', 'es2021.intl', 'es2022.array', 'es2022.error', 'es2022.intl', 'es2022.object', 
                  'es2022.sharedmemory', 'es2022.string', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 
                  'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise', 'esnext.weakref'
              ]
          }    
      }
      
    • outDir & outFile

      outDir 用来指定编译后文件所在的目录, outFile 将代码合并为一个文件.

      {
      	"compilerOptions":{
              "outDir": "./dist/",
          	"outFile": "./dist/app.js",
          }
      }
      
    • allowJs & checkJs

      allowJs 是否Js文件进行编译,默认是false, checkJs 是否检查js代码是否符合语法规范,默认是于false

      {
          "compilerOptions":{
              "allowJs": true,
              "checkJs": true,
      	}
      }
      
    • removeComments

      removeComments 是否移除注释,默认false。

      {
          "compilerOptions":{
       		"removeComments ":true,
      	}
      }
      
    • noEmit & noEmitOnError

      noEmit 不生成编译后的文件,默认false, noEmitOnError 当有错误时不生成编译后的文件.

      {
          "compilerOptions":{
              "noEmit": false,
              "noEmitOnError": true,
      	}
      }
      
    • strict类型检测

      strict 严格模式的总开关,默认false,true则开启所有严格模式.(推荐使用)

      {
          "compilerOptions":{
              "strict": true,
          }
      }
      
      • 类型检测

        alwaysStrict 用来设置编译后的文件是否使用严格模式,默认于false, 模块化的文件吗,默认开启严格模式(strict).

        {
            "compilerOptions":{
                "alwaysStrict": true,
            }
        }
        

        noImplicitAny 不允许隐式的any类型

        {
            "compilerOptions":{
                "noImplicitAny ": true,
            }
        }
        

        noImplicitThis 不允许使用不明确的this

        {
            "compilerOptions":{
                "noImplicitThis ": true,
            }
        }
        

        strictNullChecks 严格的检查空值

        {
            "compilerOptions":{
                "strictNullChecks ": true,
            }
        }
        

2.tsconfig.json示例文件:

{
  "files": [
    "./tsconfig/ts_files.ts"
  ],
  "include": [
    "./tsconfig/include/child/*"
  ],
  "exclude": [
    "./tsconfig/include/exclude/**/*"
  ],
  "extends": "./base.json",
  "compilerOptions": {
    "target": "es2015",
    "module": "es2015",
    // "lib": [],
    // outDir 用来指定编译后文件所在的目录
    "outDir": "./dist/",
    // outFile 将代码合并为一个文件
    // "outFile": "./dist/app.js",
    // allowJs 是否Js文件进行编译,默认是false
    "allowJs": true,
    // checkJs 是否检查js代码是否符合语法规范,默认是于false
    "checkJs": true,
    // removeComments 是否移除注释,默认false
    "removeComments": true,
    // noEmit 不生成编译后的文件,默认false
    "noEmit": false,
    // noEmitOnError 当有错误时不生成编译后的文件
    "noEmitOnError": true,
      
    /* 类型检测 */
    // strict 严格模式的总开关,默认false,true则开启所有严格模式
    "strict": true,
    // alwaysStrict 用来设置编译后的文件是否使用严格模式,默认于false, 模块化的文件吗,默认开启严格模式(strict)
    "alwaysStrict": true,
    // noImplicitAny 不允许隐式的any类型
    "noImplicitAny": true,
    // noImplicitThis 不允许使用不明确的this
    "noImplicitThis": true,
    // strictNullChecks  严格的检查空值
    "strictNullChecks": true,
  }
}

4. webpack

4.1.cnpm的安装

npm install -g cnpm --registry=https://registry.npm.taobao.org

4.2.安装webpack打包环境与ts

npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin clean-webpack-plugin typescript ts-loader 
or
cnpm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin clean-webpack-plugin typescript ts-loader 

4.3.编辑webpack.config.js文件

const path = require('path');		//引入一个包
module.exports = {				// webpack中的所有的配置信息都应该写在module.exports 中
    entry: "./src/index.ts",	//指定入口文件
    output: {					//指定打包文件所在目录
        path: path.resolve(__dirname, "dist"),	// 指定打包文件的目录
        filename: "bander.js",	// 打包后文件的文件名
    },
    mode: "production",			//Set 'mode' option to 'development' or 'production'
    module: {					// 指定webpack打包时要使用模块
        rules: [				// 指定要加载的规则
            {
                test: /\.ts$/,			// test指定的是规则生效的文件
                use: "ts-loader",		// 要使用的Loader
                exclude: /node_modules/,  // 要排除的文件
            },
        ]
    }
}

4.4编辑tsconfig.json

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

4.5编辑package.json

{
  "scripts": {	
    "build": "webpack"		//ADD
  },
  "devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.0",
    "ts-loader": "^9.4.1",
    "typescript": "^4.8.4",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  }
}

4.6使用webpack打包

npm run build

4.7 webpack自动生成html

const HtmlWebpackPlugin = require('html-webpack-plugin');   // 引入html插件
module.exports = {				// webpack中的所有的配置信息都应该写在module.exports 中
	...
    plugins: [					// 配置webpack插件
        new HtmlWebpackPlugin({
            title: 'this is a test case',
            template: './src/index.html',
        }),

    ]
}

处理了个错误:[webpack-cli] Error: Cannot find module 'lodash'.

4.8 webpack-dev-server

实时更新使用者的数据在页面上。

{
  "scripts": {
    "build": "webpack",
    "start": "webpack serve"
  },
	...
}

4.9 clean-webpack-plugin

每次生成的文件夹中都是新的,旧的和不是新的删除掉。

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {				// webpack中的所有的配置信息都应该写在module.exports 中
	...
    plugins: [					// 配置webpack插件
        new CleanWebpackPlugin(),
    ]
}

4.10 模块化导入文件类型(拓展名)

module.exports = {				// webpack中的所有的配置信息都应该写在module.exports 中
	...
	resolve: {
        extensions: ['.ts', '.tsx','.d.ts','.js']
    }
}

4.11 babel (兼容性)

安装依赖库

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

ts(ts-loader)->js->版本兼容(babel)

babel配置

{               // 使用配置的babel
    loader: "babel-loader", // 指定加载器
    options: {            // 设置babel
        presets: [        // 设置预定义环境
            [
                "@babel/preset-env",    // 指定环境的插件
                {                       // @babel/preset-env 的配置信息
                    "targets": {        // 要兼容的目标浏览器
                        "chrome": "55",
                        "ie": "11",
                    },
                    "corejs": "3",          // 指定corejs的版本
                    "useBuiltIns": "usage", // 使用corejs的方式 "usage":表示按需加载
                },
            ],
        ],
    },
}

4.12 webpack+babel基本配置

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

module.exports = {      // webpack中的所有的配置信息都应该写在module.exports 中
    entry: './src/index.ts',        //指定入口文件
    output: {                       //指定打包文件所在目录
        path: path.resolve(__dirname, 'dist'),      // 指定打包文件的目录
        filename: 'bander.js',                      // 打包后文件的文件名
        environment: {
            arrowFunction: false,                   // 转化时,禁用箭头函数(兼容ie)
        }
    },
    mode: "production",     //Set 'mode' option to 'development' or 'production'
    module: {               // 指定webpack打包时要使用模块
        rules: [            // 指定要加载的规则
            {
                test: /\.ts$/,      // test指定的是规则生效的文件
                use: [              // 要使用的Loader
                    {               // 使用配置的babel
                        loader: "babel-loader", // 指定加载器
                        options: {            // 设置babel
                            presets: [        // 设置预定义环境
                                [
                                    "@babel/preset-env",    // 指定环境的插件
                                    {                       // @babel/preset-env 的配置信息
                                        "targets": {        // 要兼容的目标浏览器
                                            "chrome": "55",
                                            "ie": "11",
                                        },
                                        "corejs": "3",          // 指定corejs的版本
                                        "useBuiltIns": "usage", // 使用corejs的方式 "usage":表示按需加载
                                    },
                                ],
                            ],
                        },
                    },
                    'ts-loader',
                ],
                exclude: /node_modules/,     // 要排除的文件
            },
        ],
    },
    plugins: [      // 配置webpack插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'this is a test case',
            template: './src/index.html',
        }),
    ],
    resolve: {      //用来设置引用模块
        extensions: ['.ts', '.js']
    }

}

// 加载器:谁在后面先执行

第二章 面向对象

对象的了解

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

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

一切操作都要通过对象,也就是所谓的面向对象,那么对象到底是什么呢?这就要先说到程序是什么,计算机程序的本质就是对现实事物的抽象,抽象的反义词是具体,比如:

照片是对一个具体的人的抽象,汽车模型是对具体汽车的抽象等等。程序也是对事物的抽象,在程序中我们可以表示一个人、一条狗、一把枪、一颗子弹等等所有的事物。一个事物到了程序中就变成了一个对象。

在程序中所有的对象都被分成了两个部分数据和功能,以人为例,人的姓名、性别、年龄、身高、体重等属于数据,人可以说话、走路、吃饭、睡觉这些属于人的功能。

数据在对象中被成为属性,而功能就被称为方法。所以简而言之,在程序中一切皆是对象。

1.类(class)

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

  • 定义类:

    class 类名 {
        
        属性名:类型;
        
        constructor(参数:类型){
            this.属性名 = 参数;
        }
        
        方法名(){}
    }
    
    
  • class Person {
        /**
        直接定义的属性是实例属性,需要通过对象的实例去访问:
            const per = new Person( );
            per.name
        使用static开头的属性是静态属性(类属性),可以直接通过类去获取
            Person.age
        readonly开头的属性表示一个只读的属性无法修改
        */
    
        // 定义实例属性
        name: string = "alex";
        age: number = 18
    
        // 在属性前使用static关键字可以定义类属性(静态属性)
        static canUseTools: boolean = true;
    
        // 属性前使用关键字readonly,可以限制数据为只读(提示为只读,深度为一层, 内部应该是监视数据的存储地址的变换)
        str: string = "string";
        readonly readonlystr = "string";
        hobby: Array<string> = ["write", "dirk"];
        readonly games: Array<string> = ["runing", "palying", "sung"]
    
        static readonly gratecory: Array<boolean> = [true, false, false]
    
    
        // 定义实例化方法: def function() ->: return
        instanceFuction(num1: number, num2: number): number {
            return this.age + num1 + num2
        }
    
        // 定义静态方法:@staticmethod
        static staticFuction(num1: number, num2: number): number {
            // return this.age - num1 - num2                // 类型“typeof Person”上不存在属性“age”。
            if (this.canUseTools) return num1 - num2        // static function so can use static value
            return num1 + num2
        }
    
    
    }
    
    let p = new Person();
    console.log(p);
    console.log(p.name);
    console.log(Person.canUseTools);
    p.games[0] = p.games[2];
    Person.gratecory[0] = true
    console.log(p.instanceFuction(10, 12))       // instance function through instance object called
    console.log(Person.staticFuction(11, 2))     // static function through class called
    
    

2.构造函数(constructor)

  • class Rox{
        name:string;
        age:number;
        
        constructor(name:string,age:number){
            this.name = name
            this.age = age
        }
    }
    
  • /** 
    class Gird:
        def __init__(self,name:str, age:int):
            self.name=name
            self.age=age
     */
    class Gird {
        // 定义实例变量
        name: string;
        age: number;
        // def __init__(self,name:str, age:int):
        constructor(name: string, age: number) {
            this.name = name;       //self.name=name
            this.age = age;
        }
        sing() {
            return this.name + '~~~ ' + this.age
        }
        sief() {
            return this
        }
    
    }
    

3.类的继承(extends)

  • (function () {
        /*
        -此时,Animal被称为父类,Dog被称为子类
            - 使用继承后,子类将会拥有父类所有的方法和属性
            - 通过继承可以将多个类中共有的代码写在一个父类中,
            - 这样只需要写一次即可让所有的子类都同时拥有父类中的实例属性和实例方法
            - 如果希望在子类中添加一些父类中没有的属性或方法直接加就可以
            - 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉交
            这种子类覆盖掉父类方法的形式,我们称为方法重写
    
            继承:extends
        */
    
        class Animal {
            name: string;
            age: number;
            
            constructor(name: string, age: number) {
                this.name = name;
                this.age = age;
            }
            say() {
                return "Animal is saying";
            }
        }
    
        
        class Dog extends Animal {
            say() {
                return "汪汪汪!"
            }
        }
        class Cat extends Animal {
            // overwrite
            say() {
                return "喵喵喵!"
            }
        }
    
        let dog = new Dog("旺财", 5);
        console.log(dog);
        console.log(dog.say())
    
        let cat = new Cat("小秒", 3);
        console.log(cat)
        console.log(cat.say())
    })();
    

4. 超类(父类) (super)

  • (function () {
        /*
        如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用。
        */
        class Animals {
            name: string;
            age: number;
            animalsCharacter: Array<string> = ['run', 'say', 'any'];
            static state: boolean = false;
    
            constructor(name: string, age: number) {
                this.name = name;
                this.age = age;
            }
    
            say() {
                return this.name + 'say:"it is  ' + this.age + 'age.'
            }
            run() {
                return this.name
            }
        }
    
        class User extends Animals {
            hobby: Array<string>;
    
            constructor(name: string, age: number, hobby?: Array<string>) {
                super(name, age);
                this.hobby = hobby || [];
            }
    
            say() {
                return this.name + ' say:"he is ' + this.age + '"'
            }
        }
    
        let user = new User('alex', 18, ['cat', 'study', 'games']);
        console.log(user);
        console.log(user.hobby)
        console.log(user.say())
        console.log(user.run())
        console.log(user.animalsCharacter)
        console.log(User.state)
    })();
    

5.抽象类(abstract)

  • (function () {
        // 抽象类 abstract class 类名 {}
    
        abstract class Animal {
    
            name: string;
            age: number;
    
            constructor(name: string, age: number) {
                this.name = name;
                this.age = age;
            }
    
            abstract getName(): string
            abstract getAge(): number
            abstract say(): string
            abstract run(): void
        }
    
    
        class People extends Animal {
    
            constructor(name: string, age: number) {
                super(name, age);
            }
            getName() {
                return this.name;
            }
            getAge() {
                return this.age;
            }
            say() {
                return 'hahah'
            }
            run() {
                console.log('Running...')
            }
        }
    
        let p = new People('alex', 18)
        console.log(p.getName())
    
    })();
    

6.接口(interface规范)

  • (function () {
        // type 一般用于 定义类型 或是 限制数据结构, 在ts中不能重复声明相同名称的type
    
        type myType = {
            name: string;
            age: number;
            getName(): string;
        }
        // type myType = {
        //     gender: string;
        // }
    
        let m: myType;
        function getName() { return m.name; }
        m = { name: 'alex', age: 18, getName };
        console.log(m.getName())
    
        // 通过类实现接口(接口是一种规范,所有的都是抽象的(属性+方法)对类的一种限制)
        // 实现一种规范时,使用的接口,实现一个类时,使用的是抽象类,实现一种数据结构或类型限制,使用type。
        enum Gender {
            male = 0,
            female = 1,
        }
    
        interface myInter {
            name: string;
            age: number;
            getName(): string;
        }
        interface myInter {
            gender: Gender;
        }
    
        let i: myInter;
        i = {
            name: 'alex',
            age: 18,
            gender: Gender.male,
            getName() { return i.name; }
        }
        console.log(i.getName());
    
        /*
        接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
    
        接口可以在定义类的时候去限制类的结构,
        接口中的所有的属性都不能有实际的值
        接口只定义对象的结构,而不考虑实际值
        在接口中所有的方法都是抽象方法
        */
    
    
        /* ================================================================================
            实现一个接口
            1.定义一个接口规范
            2.对定义的接口进行实现
        */
    
        // 定义接口的数据结构
        type MyData = Array<{
            name: string;
            price: number;
            count: number;
        }>
    
        // 定义接口的规范
        interface DataInterface {
    
            data: Array<{
                name: string;
                price: number;
                count: number;
            }>;
    
            get_data(): Array<{}>;
            set_data(data: MyData): void;
        }
    
        // 对接口的实现  implements (工具)
        // (通过)类 工具 (实现)接口  (接口实现类)
        class DataProvider implements DataInterface {
            data: MyData;
            constructor(data: MyData) {
                this.data = data;
            }
            get_data() {
                return this.data
            }
            set_data(data: MyData) {
                console.log(data);
            }
    
        }
    
        
        // 符合结构数据规范的数据
        let mydata: MyData = [{
            name: 'apple',
            price: 200,
            count: 50
        }]
        // 创建一个接口实例
        let d = new DataProvider(mydata)
    
        console.log(d)
        console.log(d.data)
        console.log(d.get_data())
        console.log(d.set_data(mydata))
    })();
    

7.类的封装(public、 private、protected)

  • (function () {
        /**
         * 属性的封装:
         *  - public (公开的) 修饰的属性可以在任意位置访间<修改>默认值
         *  - private (私人的) 私有属性,私有属性只能在类内部进行访间(修改)
         *      -通过在类中添加方法使得私有属性可以被外部访问
         *  - protected (保护的) 该属性只能类和子类中使用
         */
    
    
        class Person {
            public name: string;    // default
            public age: number;
    
            constructor(name: string, age: number) {
                this.name = name;
                this.age = age;
            }
        }
    
        class Apple {
            // class in parts var
            private _name: string;
            private _age: number;
    
            constructor(name: string, age: number) {
                this._name = name;
                this._age = age;
            }
            //间接的外部访问
            get name(): string { return this._name; }
            set name(value: string) { this._name = value; }
            get age(): number { return this._age; }
            set age(value: number) { this._age = value; }
        }
    
        /*
            getter方法用来读取属性
            setter方法用来设置属性
                -它们被称为属性的存取器
        */
        // 现在属性是在对象中设置的,属性可以任意的被修改,
        // 属性可以任意被修改将会导致对象中的数据变得非常不安全
    
    
        class Animal {
            private _name: string;
            private _age: number;
            protected pro_name: string = 'Animal';
    
            constructor(name: string, age: number) {
                this._name = name;
                this._age = age;
            }
        }
    
        class Dog extends Animal {
    
            constructor(name: string, age: number, private hobby: string, public getability: string) {
                super(name, age);
            }
    
            get_father_class_param(): string {
                return this.pro_name
            }
        }
    })();
    

8.泛型(< T >)

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

  • 泛型可以用于函数,类,方法等。

  • 泛型可以是类,也可以是Type,也可以是interface...

  • 泛型就是在我们使用的类型不明确时,声明一个(或多个)变量,用于表示传输或数据的类型。(泛型)

  • 泛型就像它名字一样,泛(广泛,多,不确定)型(类型)。

  • (function () {
        /**
        - 在定义函数或是类时,如果遇到类型不明确就可以使用泛型,
            - 泛型可以用于函数,类,方法等。
            - 泛型可以是类,也可以是Type,也可以是interface...
    
        - 泛型就是在我们使用的类型不明确时,声明一个(或多个)变量,用于表示传输或数据的类型。(泛型)
        - 泛型就像它名字一样,泛(广泛,多,不确定)型(类型)。
         */
    
        // 类型是确定
        let fn = function (a: number, b: number): number {
            return a * b;
        }
    
        /* 函数式 泛型 */
        // 泛型: 类型是不确定的,通过声明一个变量,用于表示这种不确定的类型 -> 泛型
        let gen = function <T>(a: T): T {
            return a
        }
        gen<string>('string');  // 手动定义泛型的准确类型
        gen('string');      // 自动识别泛型的准确类型
        let gen1 = function <T, K, V>(a: T, b: K, c: V): T { return a };
    
        // interface 泛型约束(属性)
        interface MyGen1 {
            length: number;
        }
        let myGen1 = function <n extends MyGen1>(a: n): number {
            return a.length
        }
        console.log(myGen1('STRING'))
        console.log(myGen1([]))
    
        // interface 多参数
        interface MyGen2<T, K> {
            name: T;
            age: K;
            getName(): T;
        }
        let myGen2 = function <T, K>(a: T, b: K): MyGen2<T, K> {
            let MyGen2: MyGen2<T, K> = {
                name: a,
                age: b,
                getName() { return this.name }
            }
            return MyGen2
        }
        console.log(myGen2('STRING', 18))
        console.log(myGen2('STRING', 18).getName())
    
    
    
        // interface 泛型接口(函数)  就是 function “填写部分” {}
        interface MyGen3 {
            <T>(name: T): T
        }
        let myGen3: MyGen3 = function <T>(name: T): T {
            return name
        }
        interface MyGen4 {
            <T>(length: number, value: T): Array<T>
        }
        let myGen4: MyGen4 = function <T>(length: number, value: T): Array<T> {
            return [value]
        }
        console.log(myGen4(12, 'value'))
    
    
    
        // class
    
        class Test<T>{
            constructor(public name: T) { }
        }
    
        class MyClass<MyGen5>{
            name: MyGen5
            constructor(name: MyGen5, public age: number) {
                this.name = name;
            }
            getName() { return this.name }
        }
    
        console.log(new MyClass('STRING', 18))
        console.log(new MyClass('STRING', 18).getName())
    
    
        // 通过类实现接口,通过使用泛型...
    
    })();
    

    image-20221105143927168

    image-20221105145104459

    image-20221105145135684

    image-20221105145425727

    image-20221105145820811

    image-20221105145931044

    image-20221105150046362

    image-20221105150157294

    image-20221105150305468

    image-20221105151803160

    image-20221105152134106

    image-20221105152348674

    image-20221105154202776

    image-20221105154325235

    image-20221105154415849

ts类型声明文件

ts中的类型声明文件(.d.ts)类似于python中的存根文件(.pyi),只能够声明类型,不能做具体的实现。

image-20221105154701034

image-20221105155001567

image-20221105160942477

image-20221105161523492

自己创建ts 声明类型文件

image-20221105163628984image-20221105164837919image-20221105170451616

image-20221105170316632

import {count, songName, position} from "./utils";

第三章 实战演练

贪吃蛇(snake)

1.环境搭建

- webpack
	- webpack.config.js
- ts
	- tsconfig.json
-label
	- [...]

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {      // webpack中的所有的配置信息都应该写在module.exports 中
    entry: './src/index.ts',        //指定入口文件
    output: {                       //指定打包文件所在目录
        path: path.resolve(__dirname, 'dist'),      // 指定打包文件的目录
        filename: 'index.js',                      // 打包后文件的文件名
        environment: {
            arrowFunction: false,                   // 转化时,禁用箭头函数(兼容ie)
        }
    },
    mode: "production",     //Set 'mode' option to 'development' or 'production'
    module: {               // 指定webpack打包时要使用模块
        rules: [            // 指定要加载的规则
            {
                test: /\.ts$/,      // test指定的是规则生效的文件
                use: [              // 要使用的Loader
                    {               // 使用配置的babel
                        loader: "babel-loader", // 指定加载器
                        options: {            // 设置babel
                            presets: [        // 设置预定义环境
                                [
                                    "@babel/preset-env",    // 指定环境的插件
                                    {                       // @babel/preset-env 的配置信息
                                        "targets": {        // 要兼容的目标浏览器
                                            "chrome": "55",
                                            "ie": "11",
                                        },
                                        "corejs": "3",          // 指定corejs的版本
                                        "useBuiltIns": "usage", // 使用corejs的方式 "usage":表示按需加载
                                    },
                                ],
                            ],
                        },
                    },
                    'ts-loader',
                ],
                exclude: /node_modules/,     // 要排除的文件
            },
        ],
    },
    plugins: [      // 配置webpack插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'this is a test case',
            template: './src/index.html',
        }),
    ],
    resolve: {      //用来设置引用模块
        extensions: ['.ts', '.js']
    }

}

// 加载器:谁在后面先执行

tsconfig.json

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

package.json

{
  "scripts": {
    "build": "webpack",
    "start": "webpack serve"
  },
  "devDependencies": {
    "@babel/core": "^7.19.6",
    "@babel/preset-env": "^7.19.4",
    "babel-loader": "^9.0.1",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.26.0",
    "html-webpack-plugin": "^5.5.0",
    "lodash": "^4.17.21",
    "pretty-error": "^4.0.0",
    "renderkid": "^3.0.0",
    "ts-loader": "^9.4.1",
    "typescript": "^4.8.4",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  }
}
npm i			# npm 自动识别package.json文件,自动下载依赖包

或者bash 输入

npm i webpack webpack-cli webpack-dev-server 		# webpack 相关
npm i core-js										# js核心
npm i typescript ts-loader							# ts 加载器
npm i @babel/core @babel/preset-env babel-loader	# babel 核心 
npm i renderkid pretty-error lodash					# 环境
npm i html-webpack-plugin clean-webpack-plugin		# plugin

初始完整结构:

- node_modules
- src				# 自己创建
	- index.ts	
	- index.html
- tsconfig.json
- webpack.config.js
- package.json
- package-lock.json

css样式

npm i less less-loader css-loader style-loader	

css样式兼容

npm i postcss postcss-loader postcss-preset-env

webpack.config.js

module.exports = {    
    module: {               
        rules: [          
            {               // 对less文件的设置规则
                test: /\.less$/,
                use: [
                    "style-loader",     //  style -> dom
                    "css-loader",       //  处理js中的css -> style
                    // postcss: 			css -> (兼容)css
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        "postcss-preset-env",
                                        {
                                            "browsers": "last 2 versions",
                                         }
                                     ]
                                 ]
                             }
                         }
                     },
                    "less-loader",       // 处理js中的less -> css
                 ],
             },
            ...
            ...
         ],
     },
}

2.项目界面

image-20221104175858735

// 设置变量
@bg-color: skyblue;

//清除默认样式
* {
    margin: 0;
    padding: 0;
    // 改变盒子模式的计算方式
    box-sizing: border-box;
}

body {
    font: bold 20px "Courier";
}


// 设置主窗口的样式
#main {
    width: 360px;
    height: 420px;
    background-color: @bg-color;
    margin: 100px auto;
    border: 10px solid black;
    border-radius: 40px;

    // 开启弹性盒模型
    display: flex;
    // 设置主轴的方向
    flex-flow: column;
    // 设置副轴的对齐方向
    align-items: center;
    // 设置主轴的对齐方式
    justify-content: space-around;

    // 游戏的舞台
    #stage {
        width: 304px;
        height: 304px;
        border: 2px solid black;
        position: relative;

        // 设置蛇的样式(内部div)
        #snake {
            &>div {
                width: 10px;
                height: 10px;
                background-color: #000;
                border: 1px solid @bg-color;
                // 为移动做准备(开启绝对定位)
                position: absolute;
            }
        }

        #food {
            width: 10px;
            height: 10px;
            // background-color: rgb(109, 109, 109);
            // border: 1px solid @bg-color;
            position: absolute;

            // 父类开启弹性盒
            display: flex;
            flex-flow: row wrap;
            // 设置横轴为主轴,wrap表示会自动换行
            justify-content: space-between;
            align-content: space-between;


            left: 50px;
            top: 120px;

            &>div {
                width: 4px;
                height: 4px;
                background-color: #000;
                // 使四个div旋转45度
                transform: rotate(45deg);
            }

        }

    }
    // 记分牌
    #score-panel {
        width: 300px;
        display: flex;
        justify-content: space-between;
    }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>index</title>
</head>

<body>
    <!-- 创建游戏主容器-->
    <div id="main">
        <!--设置游戏的舞台-->
        <div id="stage">
            <!--设置蛇-->
            <div id="snake">
                <!--snake内部的div表示蛇的各部升-->
                <div></div>
            </div>
            <!-- 设置食物-->
            <div id="food">
                <div></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
        </div>
        <!--设置游戏的积分牌-->
        <div id="score-panel">
            <div>
                SCORE: <span id="score">0</span>
            </div>
            <div>
                LEVEL: <span id="level">1</span>
            </div>
        </div>
    </div>
</body>
</html>

3.食物类Food

abstract class Foods {

    element: HTMLElement;

    constructor(food: string = "food") {
        this.element = document.getElementById(food)!;
    }

    abstract get X(): number;
    abstract get Y(): number;
    abstract get_random_value(): number;
    abstract change(): void;

}

class Food extends Foods {

    get X(): number {
        return this.element.offsetLeft;
    }
    get Y(): number {
        return this.element.offsetTop;
    }
    get_random_value(): number {
        return Math.round(Math.random() * 29) * 10;
    }
    get_random_coordinate(): [number, number] {
        let rand_left = this.get_random_value();
        let rand_top = this.get_random_value();
        return [rand_left, rand_top]
    }
    change(): void {
        let [rand_left, rand_top]: Array<number> = this.get_random_coordinate();
        this.element.style.left = rand_left + 'px';
        this.element.style.top = rand_top + 'px';
    }
}

export default Food;

4.积分牌类ScorePanel

abstract class ScorePanels {

    score: number;
    level: number;

    scoreElement: HTMLElement;
    levelElement: HTMLElement;

    constructor(score: number = 0, level: number = 1) {
        this.score = score;
        this.level = level;
        this.scoreElement = document.getElementById('score')!;
        this.levelElement = document.getElementById('level')!;
    }

    abstract addScore(): void;
    abstract upLevel(): void;

}


class ScorePanel extends ScorePanels {

    _subsection: number;
    _maxLevel: number;
    constructor(subsection: number = 10, maxLevel: number = 10) {
        super()
        this._subsection = subsection;
        this._maxLevel = maxLevel;
    }
    addScore(): void {
        this.scoreElement.innerText = ++this.score + '';
        if (this.level < this._maxLevel) {
            if (this.score % this._subsection == 0) this.upLevel()
        }
    }
    upLevel(): void {
        this.levelElement.innerText = ++this.level + '';
    }
    set subsection(value: number) {
        this._subsection = value;
    }
    set maxLevel(value: number) {
        this._maxLevel = value;
    }
}

export default ScorePanel;

5.蛇类 snake

abstract class SnakeFactory {

    snake: HTMLElement;
    head: HTMLElement;
    bodies: HTMLCollection;

    constructor(snake: string = "snake", head: string = "#snake > div", bodyTag: string = "div") {
        this.snake = document.getElementById(snake)!;
        this.head = this.snake.querySelector(head)!;
        this.bodies = this.snake.getElementsByTagName(bodyTag);
    }

    abstract get X(): number;
    abstract get Y(): number;
    abstract set X(value: number);
    abstract set Y(value: number);
    abstract addBody(): void;
}


class Snake extends SnakeFactory {

    constructor(public speed: number = 300, public islive: boolean = true) {
        super();
        this.speed = speed;
        this.islive = islive;
    }

    get X(): number {
        return this.head.offsetLeft
    }
    get Y(): number {
        return this.head.offsetTop
    }
    set X(value: number) {
        if (this.get_is_alive(value)) {
            if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
                if (value > this.X) {
                    value = this.X - 10;
                } else {
                    value = this.X + 10;
                }
            }

            this.moveBody()
            this.head.style.left = value + 'px';
            this.checkHeadHintBody()
        }
    }
    set Y(value: number) {
        if (this.get_is_alive(value)) {
            if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
                if (value > this.Y) {
                    value = this.Y - 10;
                } else {
                    value = this.Y + 10;
                }
            }

            this.moveBody();
            this.head.style.top = value + 'px';
            this.checkHeadHintBody()
        }
    }
    get_is_alive(value: number): boolean {
        if (value < 0 || value > 290) {
            this.islive = false;
        }
        return this.islive
    }
    addBody(): void {
        this.snake.insertAdjacentHTML('beforeend', '<div></div>')
    }
    moveBody() {
        for (let i = this.bodies.length - 1; i > 0; i--) {
            let prev_body = <HTMLElement>this.bodies[i - 1]
            let after_body = <HTMLElement>this.bodies[i]
            after_body.style.left = prev_body.offsetLeft + "px";
            after_body.style.top = prev_body.offsetTop + "px";
        }
    }

    checkHeadHintBody() {

        for (let i = 1; i < this.bodies.length; i++) {
            let bd = this.bodies[i] as HTMLElement
            if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
                this.islive = false;
            }
        }
    }
}


export default Snake;

6.游戏控制类 GameControl

import Food from './food';
import ScorePanel from './scorepanel';
import Snake from './snake';


abstract class GameControllerFactory {

    food: Food;
    scorepanel: ScorePanel;
    snake: Snake;

    constructor(food: Food, scorepanel: ScorePanel, snake: Snake) {
        this.food = food;
        this.scorepanel = scorepanel;
        this.snake = snake;
    }

    abstract init(): void;

}

class GameController extends GameControllerFactory {

    step: number = 10;
    direction: string = "";

    constructor(food: Food = new Food(), scorepanel: ScorePanel = new ScorePanel(), snake: Snake = new Snake()) {
        super(food, scorepanel, snake);
        this.init();
    }

    init(): void {
        document.addEventListener('keydown', this.keydownhander.bind(this))
        this.run()
    }
    keydownhander(event: KeyboardEvent): void {
        this.direction = event.key
    }
    get_speed(): number {
        return this.snake.speed - ((this.scorepanel.level - 1) * 30)
    }
    checkEatFood(snake: Snake) {
        if (snake.X == this.food.X && snake.Y == this.food.Y) {
            this.scorepanel.addScore()
            this.food.change();
            snake.addBody();
            snake.moveBody();
        }
    }
    run() {
        /*
            ArrowUp     || Up           top -10
            ArrowDown   || Down         top +10
            ArrowLeft   || Left         left -10
            ArrowRight  || Right        left +10
        */

        switch (this.direction) {
            case "ArrowUp":
            case "Up":
                this.snake.Y -= this.step
                break;
            case "ArrowDown":
            case "Down":
                this.snake.Y += this.step
                break;
            case "ArrowLeft":
            case "Left":
                this.snake.X -= this.step
                break;
            case "ArrowRight":
            case "Right":
                this.snake.X += this.step
                break;
        }

        this.checkEatFood(this.snake)

        this.snake.islive && setTimeout(this.run.bind(this), this.get_speed())
        if (!this.snake.islive) alert("snake is not alive, you game over!")
    }
}

export default GameController;

7. 游戏 index

import './style/index.less';
import GameController from './views/gamecontrol';
new GameController();
posted @ 2022-11-05 17:39  Redskaber  阅读(229)  评论(0)    收藏  举报