文章目录
一、什么是TypeScript
TS增加了:类型,支持ES的新特性,增加了ES不具有的新特性,丰富的配置选项,强大的开发工具。
TS开发环境:安装Node环境、npm安装typescript
npm install -g typescript
// 验证
tsc -v
// 编译
tsc helloworld.ts
二、类型声明
let a: number; // 声明另一变量a,同时指定其类型为Number
a = 11; // a的类型为number,在以后的使用过程中a的值只能是数字
a = 'hello'; // 报错,变量a的类型为number,不能赋值“string”
let b: number = 10;
let c = false; // 如果变量和赋值是同时进行的,ts可以自动对变量进行类型检测
c = true
// js中函数是不考虑参数的类型和个数的
function sum(a, b) {
return a + b;
}
console.log(sum(123, 567)); // 690
console.log(sum(123, '567')); // 123567
// ts
function sum(a: number, b: number): number { // 指定返回值类型
return a + b
}
console.log(sum(123, 567)); // 690
console.log(sum(123, '567')); // 报错,变量a的类型为number,不能赋值“string”
// 也可以直接使用字面量进行类型声明
let a: 10;
a = 10;
// a = 11; // 报错,不能将类型“11”分配给类型“10”
let b : 'male' | "famale"; // 可以用 | 来连接多个类型(联合类型) “或”
b = "famale";
b = "male";
let c : number | string;
c = 22;
c = "hello"
let d: any; // any 表示的是任意类型,相对于对该变量关闭了ts的类型检测(不建议使用)
d : 10;
d : true;
let e; // 声明变量如果不指定类型,ts解析器会自动判断变量的类型为 any(隐式的any)
e = 33;
e = "world";
let f : unknown; // 表示未知类型的值,unknown是一个类型安全的 any,不能直接赋值给其他变量
f = 555;
f = 'hhh';
let g: string;
g = d;// d的类型是any,可以赋值给任意变量
// g = f; // 报错,不能将类型“unknown”分配给类型“string”。
if(typeof f === 'string') {
g = f;
}
g = f as string // 类型断言:可以用来告诉解析器变量的实际类型
g = <string> f
function fn(): void { // void用来表示空,一函数为例,表示没有返回值
// return true // 报错,不能将类型“boolean”分配给类型“void”。
// return undefined 可以返回undefined,不能返回null,null的也是值的一种,typeof null返回值是 “object”
return
}
function fn2(): never { // never 表示永远没有返回结果
throw new Error('报错了')
}
object
let a: object;
a = {};
a = function () {} // js中万物皆对象
// {} 用来指定对象中可以包含哪些属性,{属性名:属性值,属性名:属性值}
let b: {name: string, age?: number}; // 属性名后加一个 ?表示属性是可选的
// b = {}; // 报错 类型 "{}" 中缺少属性 "name",但类型 "{ name: string; }" 中需要该属性
b = {name: '悟空', age: 18}
let i: {name: string} & {age: number}; // &表示同时 “与”
i = {name: '艾斯', age = 22}
// [propName: string]: any 表示任意类型的属性
let c: {name: string, [propName: string]: any};
c = {name: '八戒', a: 111, c: 222}
// 设置函数结构的类型声明:语法(形参:类型,形参:类型……)=> 返回值
let d: (a: number, b:number) => number;
d = function(n1: number, n2: number): number {
return n1 + n2
}
数组
let e: string[] // 限制数组存储值的类型
e = ['a', 'b', 'c']
let g: Array<number>
g = [1, 2, 3]
元组Tuple:固定长度的数组
let h: [string, number];
h = ['xixi', 123]
枚举enum:用于定义数值集合
enum Gender {
male = 0,
female = 1
}
let i: { name: string, gender: Gender }
i = {
name: '悟空',
gender: Gender.male
}
console.log(i.gender === Gender.male); // true
类型的别名
type myType = 1 | 2 | 3 | 6 | 7;
let k: myType;
let l: myType;
k = 3;
// k = 8; // 报错 不能将类型“8”分配给类型“myType”
三、编译选项
tsc index.ts -w // 开启监视index.ts单个文件
// 需要添加tsconfig.json配置文件,ts根据他的信息来对代码进行编译
tsc // 自动编译目录下所有ts文件
// tsconfig.json 中includes用来指定哪些ts文件需要被编译
/**
"include"指定哪些ts文件需要被编译 路径 ** 表示任意目录,*表示任意文件
"exclude"不需要被编译的文件目录
"extends"定义被集成的配置文件
"files"指定被编译文件的列表,只有需要被编译的文件少是才会用到
*/
{
"include": [
"./src/**/*"
],
"exclude": [
"./src/index/**/*"
],
"files": [
"index.ts"
],
"compilerOptions": {
// target 指定ts文件被编译的版本
// 可选值'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'esnext'
"target": "ES3",
// module 指定使用模块化的规范
// 可选值'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext', 'node16', 'nodenext'
"module": "System",
// 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', 'es2019.intl', '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'
"lib": ["ES6", "DOM"],
// outdir指定编译后文件所在目录
"outDir": "./dist",
// outFile将quan代码合并为一个文件 仅支持 "amd" 和 "system" 模块
"outFile": "./dist/app.js",
// allowJs是否对js文件进行编译,默认为false
"allowJs": true,
// checkJs是否检查js代码是否符合语法规范,默认是false
"checkJs": false,
// removeComments是否移除注释
"removeComments": true,
// noEmit不生产编译后的文件
"noEmit": false,
// noEmitOnError当有错误的时候不生成编译后的文件
"noEmitOnError": false,
// alwaysStrict设置编译后文件是否开启严格模式,默认false
"alwaysStrict": false,
// noImplicitAny不允许隐式any类型
"noImplicitAny": true,
// noImplicitThis不允许不明确类型的this
"noImplicitThis": true,
// strictNullChecks严格检查空值
"strictNullChecks": false,
// strict所有严格检查的总开关
"strict": false
}
}
四、使用webpack打包ts代码
npm init -y // 对项目进行初始化(注意文件名不能含中文,否则会报错)
npm i -D webpack webpack-cli typescript ts-loader
// package.json
{
"name": "05webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^9.4.2",
"typescript": "^4.9.3",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.0"
}
}
// 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"
},
module: {
// 指定要加载的规则
rules: [
{
// test指定的规则生效的文件
test: /\.ts$/,
// 要使用的loader
use: 'ts-loader',
exclude: /node-module/
}
]
}
}
// 执行 npm run build 进行打包
五、面向对象(oop)
所有的操作都要通过对象去操作就是面向对象编程。计算机程序本质就是对现实事物的抽象(比如一张照片就是对一个具体的人的抽象),一个事物到了程序中就变成了一个对象。
在程序中所有的对象都被分成了两部分数据和功能,以人为例,人的姓名、性别、年龄、身高、体重等属于数据,人可以吃饭睡觉打豆豆等属于人的功能;数据在对象中被称为属性,而功能就被称为方法,所以简而言之,在程序中万物皆对象。
5.1 类(class)
要想面向对象,操作对象,首先要使用对象,那么下一个问题就是如何创建对象。要创建对象,必须先定义类,所谓的类可以理解为对象的模型
,程序中可以根据类创建指定类型的对象。不同的类创建不同的对象,person类创建人的对象,car类创建汽车的对象等等。
5.1.1 属性和方法
// 使用class关键字来定义类
// 对象中主要包含两部分:属性 方法
class Person {
// 属性
// 实例属性:直接定义 通过对象的实例访问
readonly name : string = '艾斯';
// 类属性(静态属性):在属性前使用static关键字定义 直接通过类访问
static readonly age : number = 18;
// 方法
sayHello() {
console.log('hihihi');
}
// 静态方法 直接通过类去调用
// static sayHello() {
// console.log('hihihi');
// }
}
const per = new Person()
console.log(per.name); // 艾斯
console.log(Person.age); // 18
// per.name = '路飞';
// console.log(per.name); // 路飞
// 加上readonly开头的属性,只读无法修改
// per.name = '萨博' // 报错 无法分配到 "name" ,因为它是只读属性。
per.sayHello()
// Person.sayHello()
5.1. 2 构造函数
class Dog {
// name = "招财";
// age =8;
name: string;
age: number;
// constructor()被称为构造函数 会在对象创建时调用 对属性进行初始化
constructor(name:string, age:number) {
console.log(this); // 在实例方法中,this就表示当前的实例对象
// 在构造函数中当前对象就是当前新建的那个对象
// 可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
bark() {
alert('wangwangwang~');
// 在方法中可以通过this来表示当前调用方法的对象
console.log(this);
console.log(this.name);
}
}
const dog1 = new Dog("小黑", 2);
const dog2 = new Dog("小白", 3);
console.log(dog1);
console.log(dog2);
dog1.bark()
5.1.3 继承
// 父类
class Animal {
name: string;
age: number;
constructor(name:string, age:number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log("动物在叫");
}
}
//使Dog继承Animal类 可以在不修改父类的基础上进行扩展
// 使用继承后子类会继承父类的所有方法和属性 通过继承可以将多个类共有的代码卸载一个父类中
class Dog extends Animal{
// 添加父类没有的方法
run () {
console.log(`${this.name}在咆哮`);
}
// 如果在子类中添加了和父类相同的方法,子类会覆盖掉父类的方法,这称作为方法的重写
sayHello() {
console.log('wangwangwang~');
}
}
//使Cat继承Animal类
class Cat extends Animal{
}
const dog1 = new Dog("旺财", 5)
console.log(dog1);
dog1.sayHello()
dog1.run()
const cat1 = new Cat("招财", 6)
console.log(cat1);
cat1.sayHello()
super
class Animal {
name: string;
constructor(name:string) {
this.name = name
}
sayHello() {
console.log('miaomiaomiao~~');
}
}
class Cat extends Animal {
age: number;
constructor(name: string, age: number) {
// 如在子类中写构造函数,在子类构造函数必须父类的构造函数进行调用
super(name); // 调用父类的构造函数
this.age = age
}
sayHello() {
//在类的方法中super表示当前类的父类
super.sayHello()
}
}
const cat = new Cat("大黑", 9)
console.log(cat);
cat.sayHello()
5.1.4 抽象类、抽象方法abstract
// abstract抽象类与其他类区别不大,只是不能用来创建对象
// 抽象类就是用来被继承 生下来就是当爸爸的
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name
}
// 抽象方法 没有方法体;抽象方法只能定义在抽象类中,子类必须UI抽象方法进行重写
abstract sayHello(): void;
}
class Cat extends Animal {
age: number;
constructor(name: string, age: number) {
super(name);
this.age = age
}
sayHello() {
console.log("miaomiaomiaomiao~~~");
}
}
const cat = new Cat("大黑", 9)
cat.sayHello()
// const haha = new Animal() // 报错 无法创建抽象类的实例
5.1.5 接口
// 接口用来定义一个类的结构,用来定义一个类应该包含哪些属性和方法
// 接口可以在定义类的时候去限制类的结构,接口中的所有制都不能有实际值,接口值定义对象的结构,而不考虑实际值
// 在接口中所有方法都是抽象方法
interface myInterface {
name: string;
age: number;
}
interface myInterface {
sex: string
}
const face: myInterface = {
name: 'hhh',
age: 12,
sex: '男'
}
// 定义类时可以使用类去实现一个接口 接口其实就是一个限制
interface myInter {
name: string;
sayhello(): void;
}
class myClass implements myInter {
name: string;
constructor(name: string) {
this.name = name;
}
sayhello(): void {
console.log("nihaoyaaaa");
}
}
5.1.6 封装
public:修饰的属性可以再任意位置被访问(修改) 默认值;
private:只能在类内部进行访问修改,通过在类中添加方法使得私有属性被外部访问
class Person{
// ts中可以再施行前添加属性的修饰符
private name : string;
private age: number;
constructor(name:string, age: number) {
this.name = name;
this.age = age;
}
// // getter、setter方法用来设置属性 被称为属性的存取器
// // 定义方法,用来获取name属性
// getName() {
// return this.age
// }
// // 设置方法,用来设置name属性
// setName(value: number ) {
// // 判断年龄是否合法
// if(value >= 0) {
// this.age = value
// }
// }
// Ts中设置getter方法的方式
get _age() {
console.log('get执行了');
return this.age
}
set _age(value: number) {
console.log('set执行了');
if(value >= 0) {
this.age = value
}
}
}
const per = new Person('路飞', 11)
// console.log(per._age);
per._age = -11; // 不合法修改不了
console.log(per._age);
// 属性是在对象中设置的,属性可以再任意的被修改
// 属性可以任意被修改将会导致对象中的数据变得非常不安全
// per.name = '艾斯';
// per.setName(12)
// // per.age = 23; // 报错 属性“_age”为私有属性,只能在类“Person”中访问
// console.log(per.getName());
// console.log(per);
console.log();
protected:受保护的属性,只能在当前类和当前类的子类中使用
class A {
protected num: number;
constructor(num: number) {
this.num = num
}
}
class B extends A {
test() {
console.log(this.num);
}
}
const b = new B(345)
b.num = 22 // 报错 属性“num”受保护,只能在类“A”及其子类中访问
简化
class C {
// 直接将属性定义在构造函数中
constructor(public name: string, public age : number) {
}
}
const c = new C('xixxi', 12)
5.1.7 泛型
// 在定义函数或者类时,如果遇到类型不明确就可以使用泛型
function fn<T>(a: T): T {
return a
}
// 在函数执行的时候才能确定是什么类型
// 可以直接调用具有泛型的函数
let result = fn(10);// 不指定泛型,ts可以自动对泛型进行判断
let result2 = fn<string>('hello') // 指定泛型
function fn2<T, K>(a: T, b: K):T {
console.log(b);
return a
}
fn2<number, string>(55, 'world')