TypeScript进阶

1,244 阅读10分钟

一.介绍和安装

1. 安装相关

安装Typescript

npm install -g typescript

cnpm install -g typescript

yarn global add typescript

安装cnpm

npm install -g cnpm --registry=registry.npm.taobao.org

安装 yarn

npm install -g yarn

cnpm install -g yarn

2. 检测是否安装成功

tsc -v 如果有版本号就安装成功了。

3. 配置自动编译ts文件

使用tsc --init 生成tsconfig.json文件

修改tsconfig.json中的outDir输出文件路径

点击vscode - 终端 - 运行任务 - typescript - tsc:监视tsconfig.json

二.数据类型(指定完数据类型之后改成别的类型会报错)

1.布尔类型(boolean)

let flag:boolean = true;把flag改成别的类型会报错

注意,使用构造函数 Boolean 创造的对象不是布尔值:

let createdByNewBoolean: boolean = new Boolean(1);

// Type 'Boolean' is not assignable to type 'boolean'. // 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.

事实上 new Boolean() 返回的是一个 Boolean 对象:

let createdByNewBoolean: Boolean = new Boolean(1);

直接调用 Boolean 也可以返回一个 boolean 类型:

let createdByBoolean: boolean = Boolean(1);

注意区分大小写

2.数字类型(number)

let num:number = 123; number = 456;

// ES6 中的二进制表示法

let binaryLiteral: number = 0b1010;

// ES6 中的八进制表示法

let octalLiteral: number = 0o744;

3.字符串类型(string)

let str:string = '123'

4.数组类型(array)

es中定义数组三种方法:

第一种方式:let arr:number[] = [1,2,3];

第二种方式(泛型<>):let arr:Array《number》 = [1,2,3];

第三种方式:let arr:any[] = [1,2,'3','4'];

5.元祖类型(tuple) 属于数组的一种 给数组中每个位置指定一个类型

let arr:[string,number,boolean] = ['ts',1,true]; 如果值都是字符串 报错

6.枚举类型(enum) 如果标识符没有赋值 它的值就是下标 如果赋值了 之后的值递增

enum 枚举名{
	标识符[=整型常数]
}

enum Flag {
	success = 1,
	error = 2
}
let s:Flag = Flag.success; //console.log(s);1

enum Color{blue,red,'orange'};
let c:Color = Color.red; //console.log(c);//1

enum Color {red,blue=5,orange};
let c:Color = Color.red;  //0
let c:Color = Color.orange;  //6

7.任意类型(any)

let num:any = 123; num = 'str';

任意类型的用途:

let oBox:any = document.getElementById('box')';//如果不指定类型会报错

oBox.style.color = 'red';

在任意值上访问任何属性都是允许的:

let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);

也允许调用任何方法:

let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');

可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:

8.null undefined(定义没有赋值就是undefined)

undefined 类型的变量只能被赋值为 undefined,null 类型的变量只能被赋值为 null

let num:number; console.log(num); //报错

let num:undefined; console.log(num);//undefined

let num:number | undefined; num = 123; console.log(num);//123

let num:null; num = null;

与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:

// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u: undefined;
let num: number = u;

而 void 类型的变量不能赋值给 number 类型的变量:
let u: void;
let num: number = u;
// Type 'void' is not assignable to type 'number'.

类型推论:如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查

9.void类型:表示没有任何类型,一般用于定义方法的时候方法没有返回值

function run():void{ console.log('run); } run();

function run():number{ return 123; } run();

声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:

let unusable: void = undefined;

10.never类型:是其他类型(包括null和undefined)的子类型,代表不会出现的值。声明nerer的变量只能被never类型赋值。

let a:undefined; a = undefined;

let b:null; b = null;

let a:never; a=123;//报错

a=(()=>{throw new Error('错误')})(); //正确

三:函数

1.函数定义

//函数声明方法:
//具名函数
function run():string{
	return 'run';
}

//匿名函数:(函数表达式)
var fun = funciton():number{
	return 123;
}

//定义方法传参:
function getInfo(name:string, age:number):string{
	return `${name} --- ${age}`
}
getInfo('zhangsan',20);

var getInfo = function(name:string, age:number):string{
	return `${name} --- ${age}`
}
getInfo('zhangsan',40);

//没有返回值的方法
function run:void(){
	console.log('run');
}
run();

2.方法可选参数

es5里面方法的是实参跟行参可以不一样,但是ts中必须一样,如果不一样需要配置可选参数

function getInfo(name:string, age?:number):string{
	if(age){
		return `${name} --- ${age}`
	}else{
		return `${name} --- 年龄保密`
	}
}
getInfo('zhangsan',20);//zhangsan --- 20;
getInfo('zhangsan');//zhangsan --- 年龄保密;

注意:可选参数必须配置到参数的最后面

3.默认参数

es5里面没法设置默认参数,es6和ts中都可以设置默认参数

function getInfo(name:string, age:number=20):string{
	if(age){
		return `${name} --- ${age}`
	}else{
		return `${name} --- 年龄保密`
	}
}
getInfo('zhangsan');//zhangsan --- 20

4.剩余参数(三点运算符接受行参传过来的值)

function sum(...result:number[]):number{
	var sum = 0;
	for(let i = 0;i<result.length;i++){
		sum+=result[i]
	}
	return sum;
}
sum(1,2,3,4);//10

//把1赋值给a 剩余的赋值给result
function sum(a:number, ...result:number[]):number{
	var sum = a;
	for(let i = 0;i<result.length;i++){
		sum+=result[i]
	}
	return sum;
}
sum(1,2,3,4);//10

5.函数重载

两个或者两个以上的同名函数,但它们传入不同的参数,执行不同的结果

function getInfo(name:string):string;
function getInfo(age:number):number;
function getInfo(str:any):any{
	if(typeof str === 'string'){
		return '我叫:'+str
	}else{
		return '我的年龄:'+str;
	}
}

getInfo('张三');//我叫张三
getInfo(20);//我的年龄20

function getInfo(name:string):string;
function getInfo(name:string, age:number):string;
function getInfo(name:any, age?:any):any{
	if(age){
		return '我叫:'+name+'我的年龄:'+age
	}else{
		return '我叫:'+name
	}
}

6.箭头函数

箭头函数里面的this指向上下文

7.用接口定义函数的形状

我们也可以使用接口的方式来定义一个函数需要符合的形状:

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

四:类

1.es5中的类 静态方法 继承

类:属性+方法+原型链

function Person{
	this.name = '张三';//属性
	this.age = 20;
	//构造函数里面增加方法
	this.run = function(){ //实例方法 必须new之后才能调用
		alert(this.name+'在运动');
	}
}

//静态方法
Person.getInfo = function(){
	alert('我是静态方法')
}

//原型链下面定义属性跟方法 
原型链上面的属性会被多个实例共享 构造函数不会

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}
//实例化
var p = new Person();
console.log(p.name);//张三
console.log(p.age);//20
p.run();//张三在运动
p.work();//张三在工作
//调用静态方法
Person.getInfo(); //我是静态方法

继承:

对象冒充继承:可以继承构造函数里面的属性和方法,但是没法继承原型链上面的属性和方法 Person.call(this);

function Person{
	this.name = '张三';
	this.age = 20;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

//web类继承Person类 原型链+对象冒充组合继承模式
function Web(){
	Person.call(this); //对象冒充
}

var w = new Web();
w.run();/c张三在运动 对象冒充可以继承构造函数里面的属性和方法
w.work(); //报错 对象冒充没法继承原型链上面的属性和方法

原型链继承:可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法 web.prototype = new Person(); 但是实例化子类的时候没法给父类传参

function Person{
	this.name = '张三';
	this.age = 20;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

//web类继承Person类 原型链继承模式
function Web(){

}

Web.prototype = new Person();
var w = new Web();
w.run();//张三在运动
w.work();//张三在工作

//原型链实现继承的问题

function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

//web类继承Person类 原型链继承模式
function Web(name,age){

}

Web.prototype = new Person();
var w = new Web('李四',20);//实例化子类的时候没办法给父类传承
w.run();//undefined在运动
w.work();//undefined在工作

原型链+构造函数组合继承:完美解决上面的问题

function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

function Web(name,age){
	Person.call(this,name,age);//对象冒充继承 实例化子类可以给父类传承
}

Web.prototype = new Person();
var w = new Web('李四',20);
w.run();//李四在运动
w.work();//李四在工作

另一种方式:

function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){ 
		alert(this.name+'在运动');
	}
}

Person.prototype.sex = '男';
Person.prototype.work = function(){
	alert(this.name+'在工作');
}

function Web(name,age){
	Person.call(this,name,age);//对象冒充继承 实例化子类可以给父类传承 可以继承构造函数的属性和方法
}

Web.prototype = Person.prototype;//因为上面对象冒充已经继承了构造函数的。这边只需要继承原型链的
var w = new Web('李四',20);
w.run();//李四在运动
w.work();//李四在工作

2.ts里面的类

定义类

class Person{
	name:string;//定义属性 前面省略了public关键词
	constructor(n:string){//构造函数 实例化类的时候触发的方法
		this.name = n;
	},
	run():void{
		alert(this.name);
	}
}

var p = new Person('张三');
p.run();//张三
class Person{
	name:string;//定义属性 前面省略了public关键词
	constructor(name:string){//构造函数 实例化类的时候触发的方法
		this.name = name;
	},
	getName():string{
		return this.name;
	},
	setName(name:string):void{
		this.name = name;
	}
}

var p = new Person('张三');
p.getName();//张三
p.setName('李四');
p.getName();//李四

继承 extends super

class Person{
	name:string;//定义属性 前面省略了public关键词
	constructor(name:string){//构造函数 实例化类的时候触发的方法
		this.name = name;
	},
	run():string{
		return `${this.name}在运动`
	}
}

class Web extends Person{
	constructor(name:string){
		super(name);//初始化父类的构造函数	
	},
}	

var w = new Web('李四');
w.run();//李四在运动

继承的探讨 父类跟子类的方法一致 先在子类里面找 如果没有再去父类找

class Person{
	name:string;//定义属性 前面省略了public关键词
	constructor(name:string){//构造函数 实例化类的时候触发的方法
		this.name = name;
	},
	run():string{
		return `${this.name}在运动`
	}
}

class Web extends Person{
	constructor(name:string){
		super(name);//初始化父类的构造函数	
	},
	run():string{
		return `${this.name}在运动-子类`
	},
	work(){
		alert(`${this.name}在工作`)
	}
}	

var w = new Web('李四');
w.run();//李四在运动-子类

修饰符

public:公有 在类里面、子类、类外面都可以访问

class Person{
	public name:string;
	constructor(name:string){
		this.name = name;
	},
	run():string{
		return `${this.name}在运动`
	}
}

class Web extends Person{
	constructor(name:string){
		super(name);	
	},
	run():string{
		return `${this.name}在运动-子类`
	},
	work(){
		alert(`${this.name}在工作`)
	}
}	

//类外部访问
var p = new Person('hahaha');
p.name;//hahaha

protected:保护类型 在类里面、子类里面都可以访问,在类外部没法访问

class Person{
	protected name:string;
	constructor(name:string){
		this.name = name;
	},
	run():string{
		return `${this.name}在运动`
	}
}

class Web extends Person{
	constructor(name:string){
		super(name);	
	},
	work(){
		alert(`${this.name}在工作`)
	}
}	

//子类里面调用
var w = new Web('hahaha1');
w.work();//hahaha1在工作
//在外部里面调用
var p = new Person('111');
p.name;//报错
//当前类里面调用
p.run();//hahaha1在运动

当构造函数修饰为 protected 时,该类只允许被继承,不允许实例化

class Animal {
    public name;
    protected constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal {
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack');

// index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and
only accessible within the class declaration.

private:私有 在类里面可以访问,子类、类外部都无法访问

属性如果不加修饰符,默认就是公有public

class Person{
	private name:string;
	constructor(name:string){
		this.name = name;
	},
	run():string{
		return `${this.name}在运动`
	}
}

class Web extends Person{
	constructor(name:string){
		super(name);	
	},
	work(){
		alert(`${this.name}在工作`);//报错
	}
}	


var w = new Web('hahaha1');
//在外部里面调用
var p = new Person('111');
p.name;//报错
//当前类里面调用
p.run();//hahaha1在运动

当构造函数修饰为 private 时,该类不允许被继承或者实例化:

class Animal {
    public name;
    private constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal {
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack');

// index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class
constructor is marked as private.
// index.ts(13,9): TS2673: Constructor of class 'Animal' is private and
only accessible within the class declaration

静态属性 静态方法(不需要实例化,可直接调用,但里面没法直接调用类里面的属性 只能把属性变成静态属性)

class Person{
	public name:string;
	public age:number = 20;
	//静态属性
	static sex = '男';
	constructor(name){
		this.name = name;
	},
	run():void{
		alert(this.name);
	},
	static print(){//静态方法 
		alert('print');
	},
	//静态方法 里面没法直接调用类里面的属性 		 
	static print1(){           
	    alert('print1 '+this.age);
	},
	//静态方法 里面没法直接调用类里面的属性 只能把属性变成静态属性
	static print2(){
	    alert('print2 '+ Person.sex);
	}
}

Person.print();//print
Person.print1();//print1 undefined
Person.print2();//print2 男

多态:多态属于继承 父类定义一个方法不去实现 让继承他的子类去实现 每一个子类有不同的表现

class Animal{
	name:string;
	constructor(name:string){
		this.name = name;
	},
	eat(){ //具体吃什么不知道 具体吃什么?继承它的子类去实现,每一个子类的表现不一样
		console.log('吃的方法');
	}
}

class Dog extends Animal{
	constructor(name:string){
		super(name);
	},
	eat(){
		return this.name+'吃粮食'
	}
}

class Cat extends Animal{
	constructor(name:string){
		super(name);
	},
	eat(){
		return this.name+'吃老鼠'
	}
}

抽象类:它是提供其他类继承的基类,不能直接被实例化;用abstract关键字定义;抽象类中的抽象方法不包含具体实现并且必须派生类中实现;抽象方法只能放在抽象类中;用来定义标准。标准:Animal这个类要求他的子类必须包含eat方法

基类:父类 派生类:子类

abstract class Animal{
	public name:string;
	constructor(name:string){
		this.name = name;
	},
	abstract eat():any;//不包含具体实现并且必须派生类中实现
	run(){
		console.log('其他方法可以不实现');
	}
}

class Dog extends Animal{
	constructor(name:any){
		super(name);
	},
	//抽象类的子类必须实现抽象类里面的抽象方法
	eat(){
		return this.name+'吃粮食'
	}
}

class Cat extends Animal{
	constructor(name:any){
		super(name);
	}
}


var a = new Animal();//报错 不能直接被实例化
var d = new Dog('小花花');
d.eat();//小花花吃粮食
var c = new Cat('小花花');
c.eat();//报错 必须实现eat方法

五:接口(定义标准)

它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

赋值的时候,变量的形状必须和接口的形状保持一致。多一些属性少一些属性都是不允许的,如果不希望完全匹配一个形状,可以用可选属性,但是仍然不允许添加未定义的属性

1.属性接口(对批量方法传入参数进行json的约束) 参数的顺序可以不一样

//传入对象的约束
interface FullName{
	firstName: string;//注意;结束
	secondName: string;	
}

function printName(name:FullName){
	//必须传入对象 firstName secondName
	console.log(name.firstName+'--'+name.secondName)
}

printName('123');//报错

printName({
	age:20,
	firstName:'张',
	secondName:'三'
})//报错 多传了20 但是赋值给对象就不报错了 如下

var obj = {
	age:20,
	firstName:'张',
	secondName:'三'
}
printName(obj); //这样的方式不会报错

//传入对象的约束
interface FullName{
	firstName: string;//注意;结束
	secondName: string;
	
}

function printInfo(info:FullName){
	//必须传入对象 firstName secondName
	console.log(info.firstName+'--'+info.secondName+'--'+info.age)
}

var obj = {
	age:20,
	firstName:'张',
	secondName:'三'
}
printInfo(obj);//报错 FullName上没有age属性

2.接口可选属性

interface FullName{
	firstName: string;
	secondName?: string;	
}
function printName(name:FullName){
	//必须传入对象 firstName
	console.log(name)
}

printName({
	firstName:'张'
})

3.接口任意属性

有时候我们希望一个接口允许有任意的属性,可以使用如下方式:

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

// index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.
// index.ts(7,5): error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.
//   Index signatures are incompatible.
//     Type 'string | number' is not assignable to type 'string'.
//       Type 'number' is not assignable to type 'string'.

上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。

4.接口只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

tom.id = 9527;

// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.

上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,所以报错了。

注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

tom.id = 89757;

// index.ts(8,5): error TS2322: Type '{ name: string; gender: string; }' is not assignable to type 'Person'.
//   Property 'id' is missing in type '{ name: string; gender: string; }'.
// index.ts(13,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.

上例中,报错信息有两处,第一处是在对 tom 进行赋值的时候,没有给 id 赋值。

第二处是在给 tom.id 赋值的时候,由于它是只读属性,所以报错了。

5.函数类型的接口:对方法传入的参数以及返回值进行约束 批量约束

//加密的函数类型接口
interface encrypt{
	(key:string,value:string):string;
}

var md5:encrypt = function(key:string,value:string):string{
	//模拟操作
	return key+value;
}
md5('name','zhangsan');//namezhangsan

var sha1:encrypt = function(key:string,value:string):string{
	//模拟操作
	return key+'---'+value;
}
sha1('name','lisi');//name---lisi

6.可索引接口:数组、对象的约束(不常用)

//对数组的约束
interface UserArr{
	[index:number]:string
}
var arr:UserArr = ['aaa','bbb'];
console.log(arr[0]);//aaa

//对对象的约束
interface UserObj{
	[index:string]:string
}
var arr:UserObj = {
	name:'lisan'
}

7.类类型接口:对类的约束 和抽象类有点相似 implements实现接口

interface Animal{
	name:string,
	eat(str:string):void;
}

class Dog implements Animal{
	name:string;
	constructor(name:string){
		this.name = name;
	},
	eat(){//可以不传参数 但是必须实现eat方法
		console.log(this.name+'吃粮食');
	}
}

var d = new Dog('小黑');
d.eat();//小黑吃粮食

class Cat implements Animal{
	name:string;
	constructor(name:string){
		this.name = name;
	},
	eat(food:string){
		console.log(this.name+'吃'+food);
	}
}

var c = new Cat('小花');
c.eat('老鼠');//小花吃老鼠

8.接口扩展:接口可以继承接口

interface Animal{
	eat():void;
}
interface Person extends Animal{
	work():void;
}

class Programmer{
	name:string;
	constructor(name:string){
		this.name = name;
	},
	coding(code:string){
		console.log(this.name+code);
	}
}

class Web extends Programmer implements Person{
	constructor(name:string){
		super(name);
	},
	eat(){
		console.log(this.name+'喜欢吃馒头');
	},
	work(){
		console.log(this.name+'写代码');
	}
}

var w = new Web('艺兴');
w.work();//艺兴写代码
w.eat();//艺兴喜欢吃馒头
w.coding('写ts代码');//艺兴写ts代码

六:泛型:不仅能够支持当前的数据类型,同时也能支持未来的数据类型 传入的参数跟返回的参数一致

1.泛型函数

//T表示泛型 具体什么类型是调用这个方法的时候决定的
function getData<T>(value:T):T{
	return value;
}

getData<number>(123);//123
getData<number>('123');//报错

function getData<T>(value:T):any{
	return '122321';
}
getData<number>(123);//122321
getData<string>('这是一个泛型');//122321

2.泛型类

//实现一个最小值算法 支持返回数字跟字符串a-z两种类型

class MinClass<T>{
	public list:T[] = [];
	add(value:T):void{
		this.list.push(value)
	},
	min():T{
		var minNum = this.list[0];
		for(let i = 0;i<this.list.length;i++){
			if(minNum>this.list[i]){
				minNum = this.list[i]
			}
		}
		return minNum;
	}
}

var m1 = new MinClass<number>();//实例化类 并且制定了类的T代表的类型是number
m1.add(1);
m1.add(2);
m1.add(13);
m1.min();//1

var m2 = new MinClass<string>();//实例化类 并且制定了类的T代表的类型是string
m2.add('a');
m2.add('p');
m2.add('z');
m2.min();//a

3.泛型接口

//第一种
interface configFn{
	<T>(value:T):T;
}

var getData:configFn = function<T>(value:T):T{
	return value;
}

getData<string>('lay');//lay
//第二种
interface configFn<T>{
	(value:T):T;
}

function getData<T>(value:T):T{
	return value;
}

var myGetData:configFn<string> = getData;
myGetData('lay');//lay

4.把类当作参数的泛型类 要实现泛型接口 这个类也应该是一个泛型类

class User{
	username: string | undefined;
	password: string | undefined;
}

class MysqlDb{
	add(user: User):boolean{
		console.log(user);
		return true;
	}
}

var u = new User();
u.username = '张三';
u.password = '131343';

var Db = new MysqlDb();
Db.add(u);


class ArticleCate{
	title: string | undefined;
	desc: string | undefined;
}

class MysqlDb{
	add(info: ArticleCate):boolean{
		console.log(info);
		return true;
	}
}

var a = new ArticleCate();
a.title = '国内';
a.desc = '国内新闻';

var Db = new MysqlDb();
Db.add(a);

以上代码重复 希望只封装一次就可以检验
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
//操作数据库的泛型类
class MysqlDb<T>{
	add(info: T):boolean{
		console.log(info);
		return true;
	}
}

//想给user表增加数据
1.定义一个user类 和数据库就行映射
class User{
	username: string | undefined;
	password: string | undefined;
}

var u = new User();
u.username = '张三';
u.password = '131343';

var Db = new MysqlDb<User>();
Db.add(u);

2.定义一个ArticleCate类 和数据库就行映射
class ArticleCate{
	title: string | undefined;
	desc: string | undefined;
	constructor(params:{
		title: string | undefined,
		desc: string | undefined
	}){
		this.title = params.title;
		this.desc = params.desc;
	}
}

var a = new ArticleCate({
	title:'国内',
	desc:'国内新闻'
});

var Db = new MysqlDb<ArticleCate>();
Db.add(a);

七:模块

1.单个暴露

export var url = 'xxx';

export funciton getData():any[]{ return [ {title:'111'} ] }

import {url, getData} from 'xxx';

getData();

2.多个暴露

export {url, getData};

import {url, getData} from 'xxx';

简化名称:as关键词

import {url, getData as get} from 'xxx';

get();

3.默认导出 一个模块只能有一个defalut导出

export default getData;

import getData from 'xxx';

八:命名空间(避免命名冲突 命名空间里面的类和方法是私有的 必须导出)

export namespace A{
	interface Animal{
		name:string,
		eat(str:string):void;
	}
	
	export class Dog implements Animal{//命名空间里面的类和方法是私有的 必须导出
		name:string;
		constructor(name:string){
			this.name = name;
		},
		eat(){//可以不传参数 但是必须实现eat方法
			console.log(this.name+'吃粮食');
		}
	}
		
	export class Cat implements Animal{
		name:string;
		constructor(name:string){
			this.name = name;
		},
		eat(food:string){
			console.log(this.name+'吃'+food);
		}
	}

}

export namespace B{
	interface Animal{
		name:string,
		eat(str:string):void;
	}
	
	export class Dog implements Animal{//命名空间里面的类和方法是私有的 必须导出
		name:string;
		constructor(name:string){
			this.name = name;
		},
		eat(){//可以不传参数 但是必须实现eat方法
			console.log(this.name+'吃粮食');
		}
	}
		
	export class Cat implements Animal{
		name:string;
		constructor(name:string){
			this.name = name;
		},
		eat(){
			console.log(this.name+'吃猫粮');
		}
	}

}

import {A,B} from 'xxx';

var d = new A.Dog('小黑');
d.eat();//小黑吃粮食

var c = new B.Dog('小花');
c.eat();//小花吃粮食1

九:装饰器:特殊的类型声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。通俗的讲就是一个方法。可以注入到类,方法,属性以及参数上来扩展

1.类装饰器:普通装饰器 不可传参(不修改类的前提下,扩展类的功能)

//装饰器
function logClass(params:any){
	//params就是当前类
	console.log(params);
	//扩展属性
	params.prototype.apiUrl = '动态扩展的属性';
	//扩展方法
	params.prototype.run = function(){
		console.log('我是run方法');
	}
}
//使用装饰器
@logClass//不能加分号;
class HttpClient{
	constructor(){
	
	},
	getData(){
	
	}
}

var http:any = new HttpClient();
console.log(http.apiUrl);//动态扩展的属性
http.run();//我是run方法

2.类装饰器:装饰器工厂(可传参)

//装饰器
function logClass(params:string){
	return function(target:any){
		console.log(target);//就是当前类
		console.log(params);//传入的参数 http://www.itying.com/api		
		//扩展属性
		target.prototype.apiUrl = params;
		//扩展方法
		target.prototype.run = function(){
			console.log('我是run方法');
		}
	}
}
//使用装饰器
@logClass('http://www.itying.com/api')//不能加分号;
class HttpClient{
	constructor(){
	
	},
	getData(){
	
	}
}

var http:any = new HttpClient();
console.log(http.apiUrl);//http://www.itying.com/api
http.run();//我是run方法

//重载构造函数

//装饰器
function logClass(target:any){
	//target就是当前类
	console.log(target);
	return class extends target{
		apiUrl:any = '我是修改后的数据';
		getData(){
			console.log(this.apiUrl+'---');
		}
	}
}
//使用装饰器
@logClass//不能加分号;
class HttpClient{
	public apiUrl:string | undefined;
	constructor(){
		this.apiUrl = '我是构造函数里面的apiUrl';
	},
	getData(){
		console.log(this.apiUrl);
	}
}

var http = new HttpClient();
http.getData();//我是修改后的数据---

3.属性装饰器

接受两个参数:

1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象

2.成员属性名

//类装饰器
function logClass(params:string){
	return function(target:any){
		console.log(target);//就是当前类
		console.log(params);//xxx
	}
}

//属性装饰器
function logProperty(params:any){
	return function(target:any,attr:any){
		console.log(target);//就是当前类的原型对象
		console.log(attr);//url
		target[attr] = params;//修改属性
	}
}

//使用装饰器
@logClass('xxx')//不能加分号;
class HttpClient{
	@logProperty('http://www.itying.com/api')
	public url:any | undefined;
	constructor(){
	
	},
	getData(){
		console.log(this.url);
	}
}

var http:any = new HttpClient();
http.getData();//http://www.itying.com/api

4.方法装饰器(用来监视、修改或者替换方法定义)

接受三个参数:

1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象

2.方法的名称

3.成员的属性描述符 方法的描述

//方法装饰器
function get(params: any){
	return function(target:any,methodsName:any,desc:any){
		console.log(target);//就是当前类的原型对象
		console.log(methodsName);//getData
		console.log(desc.value);//当前的方法
		//扩展属性跟方法
		target.apiUrl = 'xxx';
		target.run = function(){
			console.log('我是run方法');
		}
		//修改getData方法 把装饰器方法里面传入的所有参数改为string类型
		//1.保存当前的方法
		var oMethod = desc.value;
		desc.value = function(...args:any[]){
			args = args.map((value)=>{
				return String(value)					
                         })
			oMethod.apply(this,args);//修改getData方法 如果没有这个就是替换getData方法
		}
	}
}

class HttpClient{
	public url:any | undefined;
	constructor(){
	
	},
	@get('http://www.itying.com/api')
	getData(...args:any[]){
		console.log(args);
		console.log('我是getData里面的方法');
	}
}

var http:any = new HttpClient();
console.log(http.apiUrl);//xxx
http.run();//我是run方法

http.getData(123,'xxx')//['123','xxx'] 我是getData里面的方法

5.方法参数表达式(为类的原型添加一些元素数据)

接受三个参数:

1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象

2.方法的名字

3.参数在函数参数中的索引

function logParams(params:any){
	return function(target:any,methodName:any,paramsIndex:any){
		console.log(params);//xxx
		console.log(target);//原型对象
		console.log(methodName);//getData
		console.log(paramsIndex);//0
		
		target.apiUrl = params;
	}
}
class HttpClient{
	public url:any | undefined;
	constructor(){
	
	},
	getData(@logParams('xxxx') uuid:any){
		console.log(uuid);//123456
	}
}

var http:any = new HttpClient();
http.getData(123456);
console.log(http.apiUrl);//xxxx

6.执行顺序(属性>方法>方法参数>类;如果有多个同样的装饰器,先执行后面的)

//装饰器
function logClass1(params:any){
	return function(target:any){
		console.log('类装饰器1')
	}
}
function logClass2(params:any){
	return function(target:any){
		console.log('类装饰器2')
	}
}

function logAttribute(params?:string){
	return funciton(target:any,attrName:any){
		console.log('属性装饰器')
	}
}

function logMethod(params?:string){
	return funciton(target:any,methodName:any,desc:any){
		console.log('方法装饰器')
	}
}

function logParams1(params?:string){
	return funciton(target:any,methodName:any,paramsIndex:any){
		console.log('方法参数装饰器1')
	}
}

function logParams2(params?:string){
	return funciton(target:any,methodName:any,paramsIndex:any){
		console.log('方法参数装饰器1')
	}
}

//使用装饰器
@logClass1('http://baidu.com')
@logClass2('xxx')
class HttpClient{
	@logAttribute()
	public apiUrl: string | undefined;
	constructor(){
	
	},
	@logMethod()
	getData(){
		return true;
	},
	setData(@logParams1() attr1:any,@logParams2() attr2:any){
	
	}
}

var http:any = new HttpClient();
//属性装饰器 方法装饰器 方法参数装饰器2 方法参数装饰器1 类装饰器2 类装饰器1

十:数组的类型

在 TypeScript 中,数组类型有多种定义方式,比较灵活。

1.「类型 + 方括号」表示法

最简单的方法是使用「类型 + 方括号」来表示数组:

let fibonacci: number[] = [1, 1, 2, 3, 5];

数组的项中不允许出现其他的类型:

let fibonacci: number[] = [1, '1', 2, 3, 5];

// Type 'string' is not assignable to type 'number'.

数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:

let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8');

// Argument of type '"8"' is not assignable to parameter of type 'number'.

上例中,push 方法只允许传入 number 类型的参数,但是却传了一个 "8" 类型的参数,所以报错了。这里 "8" 是一个字符串字面量类型,会在后续章节中详细介绍。

2.数组泛型

我们也可以使用数组泛型(Array Generic) Array 来表示数组:

let fibonacci: Array<number> = [1, 1, 2, 3, 5];

3.用接口表示数组

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。

虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。

不过有一种情况例外,那就是它常用来表示类数组。

4.类数组

类数组(Array-like Object)不是数组类型,比如 arguments:

function sum() {
    let args: number[] = arguments;
}

// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.

上例中,arguments 实际上是一个类数组,不能用普通的数组的方式来描述,而应该用接口:

function sum() {
    let args: {
        [index: number]: number;
        length: number;
        callee: Function;
    } = arguments;
}

在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 length 和 callee 两个属性。

事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:

function sum() {
    let args: IArguments = arguments;
}

其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:

interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}

5.any 在数组中的应用

一个比较常见的做法是,用 any 表示数组中允许出现任意类型:

let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

参考链接:

TypeScript 视频教程

TypeScript 入门教程

TypeScript安利指南 这个下面有各种传送门