# 基本介绍

  • TypeScript是微软2012年10月开发的一个开源的编程语言
  • TypeScript是JavaScript的一个超集,扩展了JavaScript,为它增加了类型支持
  • TypeScript通过TypeScript编辑器或Babel转译为JavaScript代码
  • 可以运行在任何浏览器,任何操作系统,任何可运行JavaScript的地方 TS

# 环境搭建

  • 安装Node.js,TypeScript源码需要进行编译后才能运行,Node.js提供了编译环境
node -v
npm -v
  • 使用npm包管理工具下载TypeScript并全局安装,安装成功后就可以通过tsc命令编译TypeScript源码
npm install -g typescript
  • 可以通过tsc -v命令查看当前TypeScript版本

# 使用方式

# tsc命令

  • 使用命令将ts文件转为js文件,默认转换为es5的代码
tsc test.ts
  • 通过监视的方式自动监听文件的变化
tsc test.ts -w
tsc -w

# 配置tsconfig.json

通过增加tsconfig.json文件,运行tsc命令会将该目录下所有的ts文件转为js文件

tsc --init # 增加tsconfig.json文件
tsc

# 设置vscode

  • 需要先配置tsconfig.json文件的outDir
  • 点击vscode上方的终端(T),选择运行任务
tsc watch # 是自动监听,下次保存文件就自动编译
tsc build # 是编译一次

# 使用ts-node

首先全局安装ts-node,然后直接运行ts-node 启动文件名
提供了直接运行typescript的能力

npm install -g ts-node
ts-node demo.ts

# 配置文件

# 忽略TS校验

//  @ts-ignore
window.globalMsg(err)

# 文件配置

compilerOptions同级的

{
	"compilerOptions":{
		
	},
	// 指定哪些需要被编译
	"include":[
		"./src/**/*" // 当前目录src下所有文件夹下的所有文件
	],
	// 指定哪些不需要被编译
	"exclude":[
		"node_modules",
		"**/*.ts"
	],
	// 指定单独的文件需要编译
	"file":[
		"app.ts",
		"foo.ts"
	]
	// 继承其他的配置文件
	"extends":""
}

# 编译配置

compilerOptions内部的

{
	"compilerOptions":{
		"target":"es2016", // 可选项es5 es2015=>es6 es2016=>es7等
		// 设置成es6时需要在packagejson中配置"type":"module"
		"modules":"commonjs", // 生成的模块的规范 commonjs、amd、cmd、system、es5、es6
		"lib":"dom", // 编译时引入的js库,默认会带有必要的
		"outDir":"./dist/js", // 输出目录
		"outFile":""; // 多个ts文件合并输出到一个js文件,只有在amd和system规范下才能使用
		
		"allowJs":false // 是否允许编译js文件 默认false
		"checkJs":false // 是否允许检查js文件 默认false
		"removeComments":false // 是否移除注释 默认false
		"noEmitOnError":false // 有错误时是否生成js文件 默认false
		"noEmit":false // ts文件不生成js文件 默认false
		"alwaysStrict":true // 编译后的文件是否使用严格模式 默认true
		"strict":false // 严格检查总开关 默认false
		"strictNullChecks":false // 是否检查空值
	}
}

# 类型声明

# 常用类型

let a:boolean = false;
let b:string = "abc";

let c:number = 2;
let num1:number = 0b1010; // 二进制
let num2:number = 0o1010; // 八进制
let num3:number = 10; // 十进制
let num4:number = 0x000d; // 十六进制(0-9、a-f)

//  字面量,直接用值作为类型
let a:10; // 此时a只能是10,不能修改,相当于常量
let a2:'sex' | 'girl'; // a2的值要么是sex要么是girl
let state: 1|2|3|4|5;

// null、undefined、any、unknown
let aa:null = null; // 对象缺失,aa的值只能是null
let bb:undefined = undefined; // 未定义的值,bb的值只能是undefined

let dd:any; // 任意类型,不推荐使用,绕过了类型校验
let ee; // 声明变量不指定类型,ts解析器会自动判断为any类型
dd = 10; dd = "abc"; dd=false;

let ff:unknown; // 和any类似,但是比any安全,应为它不能再赋值给其他变量
ff = 10; ff = "abc"; ff=false;
let gg:string;
gg = ff; // 这是错误的
dd = ff; // 这是正确的,它可以再赋值给unkown类型和any类型


// 数组,数组的元素一般数据类型都一致,不一致的一般是元组
let arr1:string[];
let arr2:number[];
let matrix1: number[][] = [[1, 2], [3, 4]]; // 多维数组
// 泛型方式
let arr3:Array<string>; // 同上
let arr4:Array<number>; // 同上
let matrix2: Array<Array<number>> = [[1, 2], [3, 4]]; // 多维数组
// 元组,固定长度的数组,每一位的类型要对应
let arr1:[string,string,number] = ["a","b",1];


// 对象:object、Object、{},object更常用
let obj:object = {a:1};
let arr:object = [1];
// 报错 不能将number、string等基础数据类型分配给object
let num:object =20; // 报错
let str:object ='abc'; // 报错
// 正确的 可以分配给Object
let num:Object =20; 
let str:Object ='abc';
let bool:Object =true;
// {}等效于Object
let num:{} =20; 
let str:{} ='abc';
let bool:{} =true;

let obj1:{name:string, age:number}; // 声明对象
let obj2:{name:string, age?:number}; // 加上?表示属性可选
let obj3:{name:string, [propName:string]:any}; // propName:其余属性,属性名是string类型

注意

  • 各基础类型首字母是小写的
  • 对象的常用类型是object,object 不包含基础数据类型,0bject 包含基础数据类型
  • const 定义的常量值就是其类型
  • any不会检验类型,unknow会检验类型(第一次使用的类型)

# 联合类型和交叉类型

// 联合类型
let a1:string | number; // a1可以是string类型 也可以是number类型
let arr:(string|number)[] = ['abc',123]; // 申明数组
let a2:1|'2'; // a2为常量可以是1或者是'2'

let obj:{a:1}|{b:'2'}; // 要么a有属性,要么b有属性,不能有其他属性
obj = {a:1};
obj = {a:1,b:'2'};
obj = {b:'2'};

// 交叉类型,需要同时满足多个条件,常用于对象
let b1:{name:string}&{age:number}; // 一般不会这么写
let obj:{name:string,age:number} & {height:number}; // 必须有name、age、heigh的属性
obj = {name:'zhangsan',age:17,height:1.80}

// 联合交叉类型,&的执行顺序优于|
let obj:{name:string} & {age:number} | {name:number} & {age:string}
obj = {
	name:'zhangsan',
	age:10
}
obj = {
	name:20,
	age:'10'
}

# 别名 type 用于重复申请类型时

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

type myFun=(a:number,b:number) => number;
let fun:myFun = (m:number,n:number) =>m*n;

# 枚举 enum

enum sex{
	man = 0,
	women = 1
}
sex.man = 0;

# 函数类型

function fun():void{ } // 返回空值
function fun():never{ } // 永远不会有返回值
function fun(num1:number,str1:string):string{ }
function fun(num1:number,str1?:string):string{ } // 可空参数
function fun(num1:number,str1:number = 10):string{ } // 默认值参数
function fun(...arg:string[]):string{ } // 剩余参数,可以接收任意参数
async function windForce(areaName:string){ } //  异步函数类型

// 声明箭头函数,箭头表示返回类型
let fun1:(num1:number,str1:number) => number = function(n1:number,n2:number){return n1*n2}; 
let windForce = async(areaName) => {} //  异步函数类型

// 函数的声明、函数的重载
function add(a:string ,b:string):string
function add(a:number ,b:number):number
function add(a:string|number,b:string|number):string|number{
	if(typeof a=='string' &&typeof b=='string'){
		return x+y // 字符串拼接
	}
	if(typeof a=='number' &&typeof b=='number'){
		return x+y // 数字相加
	}
}
// 函数调用
add('abc','efg')
add(123,456)

# 接口类型

// 定义接口类型给对象使用
interface MyObj{
	name:string;
	age:number;
	height:number;
}
let obj:MyObj = {
	name:'zhangsan',
	age:17,
	height:1.80
}

// 定义接口给数组使用
interface MyArr{
	// [idx:number]下表类型:值类型
	[idx:number]:number|string
}
let arr:MyArr = [1,2,3,'4','5']

// 定义接口给函数使用
interface MyFn{
	// 形参及类型:返回值类型
	(p:string,a:number):void
}
let fn:MyFn = (p:string,a:number) =>{}
fn('',1)

// 接口继承
interface MyObj1 extends MyObj{
	phone:string
}
let p:MyObj1 = {
	name:'zhangsan',
	age:17,
	height:1.80,
	phone:'13123456789'
}

// 接口同名会自动合并
interface MyObj{
	phone:string
}
let p:MyObj = {
	name:'zhangsan',
	age:17,
	height:1.80,
	phone:'13123456789'
}

// 接口属性缺失
interface MyObj{
	name:string;
	age?:number; // 表示这个属性可以缺省
	height:number;
}
let p:MyObj = {
	name:'zhangsan',
	height:1.80
}

// Window接口类型,常定义在global.d.ts文件中
interface Window{
	myname:string
}
function Person(this:Window,name:string){
	this.myname = name
}
// window是Window类型的对象,可以省略
window.Person('zhangsan')

注意

类型首字母是大写的

# 泛型

// 类型参数化
function fn<T>(n:T):T{
	return n;
}
fn<number>(100);
fn<boolean>(true);
fn<'hello'>('hello');

// 多个参数
function fn<M,N>(a:M,b:N):M{
	return n;
}
fn<number,string>(100,'abc');
fn<boolean,string>(true,'abc');
fn<'hello',string>('hello','abc');

// 泛型别名,设置默认类型G=number
type ObjType<N,G=number> = {name:N,getName:()=>G}
let obj:ObjType<string,number>={
	name:'',
	getName(){
		return 1
	}
}

// 泛型接口,extends 泛型约束,N只能接受字符串或数字
interface Person<M,N extends string|number>{
	name:N
	getName:()=>G
}
let obj2:Person<string,string> ={
	name:'2',
	getName(){
		return 'x'
	}
}

# Promise的使用

// Promise返回的正确类型
interface ResolveObj{
	code:number;
	data:{a:number,b:number};
	message:string
}
let p:Promise<ResolveObj> = new Promise((resolve,reject)=>{
	resoleve({
		code:0,
		data:[{a:1,b:2},{a:11,b:22}],
		message:''
	})
})
p.then(res=>{
	if(res.code==0){
		res.data.map(item=>item.a)
	}
})

# 工具类型

interface Obj{
	name:string;
	age:number;
	height?:number
}
// Partial 部分的
// type Partial<T> = { [P in keyof T]?: T[P] | undefined; }
let obj1:Partial<Obj> ={
	name:'zhansan'
	// age可以不用定义
	// age:undefined
}

// Required 必须的,不可缺少的
// type Required<r> = { [P in keyof T]-?: T[P]; }
let obj1:Required<Obj> ={
	name:'zhansan'
	// 即使可以省略,但在Required下必须定义
	height:1.80
}


interface Obj{
	name:string;
	age:number[idx:number]:number|string;
	[idx:string]:number|string;
}
//  keyof 后面一般跟接口,表示接口的这些属性名之一
type Ptype = keyof Obj;
let p1:Ptype
p1 = 'name'
p1 = 'age'
p1 = 1
p1 = '123'

# 类型断言

通过类型断言的方式告诉编译器,"相信我,我知道自己在干什么"--断定

  • 尖括号语法
let aa:any = "abc";
let bb:number = (<string>aa).length;
  • as语法
let aa:any = "abc";
let bb:number = (aa as string).length;
  • 推断常用类型转化为字面值类型,只能为当前值,不能赋其他值
// as const 类似于转化为const
let str = 'abc' as const; 
let str = <const>'abc';
// 等同于,而且不能赋其他值
let str:'abc' = 'abc'
  • 推断数组转化为元组,只读的数组
// 只能读取,不能赋其他值
let arr = ['abc',123] as const;
let arr =<const>['abc',123];
  • 推断对象转化为只读
let user = { name:'abc',age:123 } as const
  • 解构赋值中使用
function ew(){
	let str:string = 'abc';
	let fun = (a:number,b:number):number =>a+b;
	return [str,fun];
}
// 不能直接使用
let [nStr,nFun] = ew();
console.log(nFun(a:100,b:100));

// 以下的方式可以
let [nStr,nFun] = ew() as [string,function];
// 修改返回值的类型
return [str,fun] as [string,function];
return [str,fun] as [typeof str,typeof fun];
return [str,fun] as const;

console.log(nFun(a:100,b:100));
  • 非空断言操作符 !
let name:string;
// 此时name为空则会报错
console.log(name.trim())
// 非空断言操作符 !  可以消除编辑器 当nus为undefined时候报错
// 使用时注意 保证name不为undefined,否则运行时会报错
console.log(nus!.trim())

# 类的使用

面向对象的编程思想

class Person {
	name:string;
	age:number;
	constructor(name:string,age:number){
		this.name=name;
		this.age=age;
	}
	say(){
		console.log(this.name+'说话')
	}
}
const p1=new Person(name:'张三',age:20);

注意

具有相同名称的类和接口等同于具有两个具有相同名称的接口,如果该接口的两个实例重新声明具有不同类型的相同成员,则将发生合并冲突

# 类的访问修饰符

public、private、protected,参数属性:readonly

class Person {
	public name:string; // 默认的
	private age:number; // 只能在类的内部使用
	protected sex:string; // 只能在类的内部和它的子类使用
	readonly phone:string; // 类的内部和外部都不能修改
	public constructor(name:string,age:number,sex:string,phone:string){
		this.name=name;
		this.age=age;
		this.sex=sex;
		this.phone=phone;
	}
	public say(){
		this.phone='18560151116'; // 错误,不能修改
		console.log(this.name+'说话')
	}
}
const p1=new Person(name:'张三',age:20);
console.log(p1.age) // 错误,类的外部无法使用
console.log(p1.sex) // 错误,类的外部无法使用

# 静态static

可以修饰方法和属性

class Person {
	static name:string;
	age:number;
	constructor(name:string,age:number){
		this.name=name;
		this.age=age;
	}
	static study(){
		console.log(this.name+'学习')
		// 错误 静态方法中不能访问非静态的成员
		console.log(this.age+'年龄')
	}
	say(){
		console.log(this.age+'年龄')
		// 错误 非静态方法中不能访问静态的成员
		console.log(this.name+'说话')
	}
}
const p1=new Person(name:'张三',age:20);
//  可以访问对象中的非静态方法
p1.age 
p1.say() 

Person.name
Person.study()

# 类的继承

class Studen extends Person{
	school:string;
	constructor(school:string,name:string,age:number){
		this.school=school
		// 调用父类的构造函数
		super(name,age);
	}
	// 重写父类方法,使用相同的方法名
	say(){
		console.log(this.name+'小声说话')
	}
	listen(){
		// 在子类方法中调用父类的方法
		super.say();
		console.log(this.name+'在听课')
	}
	study(){
		console.log('在'+this.school+'学习')
	}
}
const s1=new Student(school:'十一中',name:'张三',age:20)
s1.say() //  张三小声说话,重写父类的方法后执行子类的方法

# 类的多态

接口的多种不同的实现方式即为多态

interface USB {
	start():void;
	run():void;
	end():void;
}
class shubiao implements USB {
	start(){ console.log('start');}
	run(){ console.log('run');}
	end(){ console.log('end');}
}
class jianpan implements USB {
	start(){ console.log('start');}
	run(){ console.log('run');}
	end(){ console.log('end');}
}

# 抽象类和接口

如果了一个类中的一个方法是抽象方法,该类必须为抽象类,抽象类不能实例化

// 抽象类
abstract class Person {
	abstract name:string;
	age:number;
	constructor(name:string,age:number){
		this.name=name;
		this.age=age;
	}
	// 抽象方法,没有方法体
	abstract say():void;
}
// 抽象类的子类,如果子类没有实现抽象方法,子类也必须是抽象类
class Student extends Person{
	name:string = 'zhangsan';
	// 重写抽象方法
	say():void{
		
	}
	getName(){
		return this.name
	}
}

let m1 = new Student();
console.log(m1.getName());
console.log(m1.getAge());

接口是一种特殊的抽象类,接口中的成员都必须是公有的

interface  IDemo1 {
	hello:string;
	fun1():void;
}
interface  IDemo2 {
	// 箭头函数的方式
	fun2(name:string)=>void;
}
// 接口的继承
interface  IDemo3 extends IDemo1,IDemo2 {
	fun3(name:string)=>void;
}
// 接口的实现 implements IDemo1,IDemo2
class Demo implements IDemo3 {
	hello:string='demo';
	fun1(){
		
	},
	fun2('zhansan'){
		
	}
	fun3('zhansan'){
		
	}
}

# 命名空间

目的是为了解决成员名称相同的问题

  • 同一个ts文件中的使用
namespace  One {
	// 在命名空间外面使用需要导出 export
	export function add(n:number,m:number):number{
		return n+m;
	}
}
// 调用
One.add(n:10,m:10)
  • 不同的ts文件中的使用
// test.ts
export namespace  One {
	export function add(n:number,m:number):number{
		return n+m;
	}
}
// 调用
import { test } from './test'
One.add(n:10,m:10)
  • 嵌套的命名空间
namespace  One {
	export namespace Two{
		export function add(n:number,m:number):number{
			return n+m;
		}
	}
	export function add(n:number,m:number):number{
		return n+m;
	}
}
// 调用
One.Two.add(n:2,m:2)

# 声明文件

在调用第三方库的类和方法时,有时无法使用TypeScript的功能,此时需要将这些库里函数和方法体去掉后只保留导出类型声明而产生的.d.ts文件

// test.js
let host='www.1ge0.com';
function add(m,n){
	m+n
}
// es5种类的写法
function person(name,age){
	this.name = name;
	this.age = age;
}


// test.d.ts  在项目的任何地方新增个.d.ts文件
declare let host:string
declare let function add(m:number,n:number):number;
declare class person{
	name:string;
	age:number;
	constructor(name:string,age:number):void;
}


// 第三方的包
declare module 'lodash' {
	export function join(arr:any[]):void;
}
// 通过命名空间,或者是安装 npm install @types/jquery
declare namespace $ {
	export function get(url:string,fn:(data:object)=>void):any
}
// 图片
declare module '*.png';


// index.ts
// 如果不加d.ts文件此时编译是出错的,找不到host
console.log(host)
console.log(add(m:1,n:1))
const p =new person(name:'abc',age:20);
// 第三方的包
import lodash from 'lodash'
lodash.join(['abc','def']);
// 通过命名空间
$.get(url:'www.1geo.com',fn:function(data:object){
	console.log(data);
});
// 图片
import logo from './sec/img/logo.png'

# 设计模式

# 单例模式

使用的都是同一个对象

class SoundManager{
	static Instance = new SoundManager();
	private constructor(){
		
	}
}
// 使用单例
SoundManager.Instance

// 或者
class SoundManager{
	private static instance:SoundManager;
	private constructor(){
		
	}
	static Instance(){
		// 懒加载,当前单例是否产生
		if(!SoundManager.Instance){
			SoundManager.Instance = new SoundManager()
		}
		return SoundManager.Instance;
	}
}
// 使用单例
SoundManager.Instance

# 代理/委托模式

interface ICalc{
	calc(num1, num2):number;
}
class Npc1 implements ICalc{
	calc(num1, num2){
		return num1 + num2;
	}
}
class Npc2 implements ICalc{
	calc(num1, num2){
		return num1 - num2;
	}
}
class Person{
	delegate ICalc;
	GetNum(num1, num2){
		// 获取num1和num2计算后的结果
		let num = this.delegate.calc(num1, num2);
		document.write(num);
	}
}
let person = new Person();
// 设定一个代理
person.delegate = new Npc1();
person.GetNum(3, 4);

# 观察者模式

interface IObserver{
	nameChanged(newName);
}
class Person{
	private _name:string;
	// 所有的观察者
	observers:Array<IObserver> = new Array<IObserver>();
	
	set name(value){
		this._name = value;
		// 发生变化
		// 遍历观察者数组,给所有的观察者发送消息
		for(let observer of this.observers){
			observer.nameChanged(this._name);
		}
	}
	get name(){
		return this._name;
	}
}
class Test implements IObserver{
	nameChanged(newName){
		document.write('名字发生了变化'+ newName);
	}
}

let person = new Person();
let test = new Test();
// 设置位监听对象
person.observers.push(test);
person.name = '哈哈哈';
// 输出 => '名字发生了变化哈哈哈'

# 工厂模式

enum CarType{
	Bmw,
	Audi,
	Benz
}
class Car{
	name:string;
	// 工厂方法
	static Create(carType:CarType):Car{
		let car:Car;
		switch(carType){
			case CarType.Bmw:
				car = new Bmw();
				break;
			case CarType.Audi:
				car = new Audi();
				break;
			case CarType.Benz:
				car = new Benz();
				break;
		}
	}
}
class Bmw extends Car{ }
class Audi extends Car{ }
class Benz extends Car{ }
// 使用
let bmw = Car.Create(CarType.Bmw);