一、介绍
1.typescript是由微软开发的一款开源的编程语言
2.ts是js的超级,拓展了js语法,更像java/c#这样面向对象语言,更适合开发大型项目。
3.谷歌也在大力支持ts,谷歌的angular2x+ 就是基于ts语法的。
4.最新的Vue ,React 也可以集成ts。
二、安装和编译
1.安装:npm install -g typescript
2.生成配置文件:tsc --init 创建tsconfig.json 文件(eg:可修改输出地址“outDir”:“./js”,等配置)
3.编译:tsc hello.ts (hello.ts 是自己建的ts名)
4.点击菜单栏 任务-运行任务 点击tsc 监视-tsconfig.json ,然后就可以自动生成代码了。
三、ts的数据类型
1.布尔类型(boolean)
2.数字类型(number)
3.字符串类型(string)
4.数组类型(array)
5.元祖类型(tuple)
6.枚举类型(enum)
7.任意类型(any)
8.null 和 undfined
9.void 类型
10.never类型
四、各类型的用法
1.布尔类型(boolean)
let flag:boolean = true; flag =false;
2.数字类型(number)
3.字符串类型(string)
let str:string = '';
str = 'I am string';
4.数组类型(array)
let arr:string[]=[]; arr = ['1','2','3','4'];
or
let arr:Array<number> =[1,2,3,4];
5.元祖类型(tuple)
//已知数组元素的个数,并且知道每个元素的类型
let tupleArr :[string,number] =['lalala',2];
6.枚举类型(enum)
enum Color{ red = 1, blue, orange = 5, green = 7 } let redNum:Color = Color.red //1 let blueNum:Color = Color.blue //2 let orangeNum:Color = Color.orange //5 let red:string = Color[1] //red let blue:string = Color[2] //blue let orange:string = Color[5] //orange
7.任意类型(any)
let notSure:any = 4; notSure = 'maybe a string instead'; notSure = false; //okay
8.null 和 undfined
//strictNullChecks标记的启用是在tsconfig.json文件里进行配置。 // { // "compilerOptions": { //编译选项,可以被忽略,这时编译器会使用默认值 // "strictNullChecks": false, //在严格的null检查模式下,null和undefined值不包含在任何类型里,只允许赋值给void和本身对应的类型。 // } // } //"strictNullChecks": false, //默认情况下,null 和 undefined 是其它类型的子类型,可以赋值给其它类型,如number类型,此时,赋值后的类型会变成 null 或 undefined。 let hh: number; hh = null; //ok的 hh = undefined; //ok的 //"strictNullChecks": true, let ss: number; ss = null; //提示不可以 ss = undefined; //提示不可以
//定义没有赋值就是undifined;
let aa :string | undifined;
console.log(aa) //undefined
//一个元素可能是number,null,undifined
let num: number | null | undefined
num=123;
9.void 类型
//表示方法没有返回任何类型 function run(): void { console.log(111); } run() //表示传参是number类型,函数返回值也是number类型 function sum(num:number): number { console.log(111); return num + 123 } sum(12);
10.never类型
//never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。
// 即使 any也不可以赋值给never。通常表现为抛出异常或无法执行到终止点(例如无线循环)。比如: let x: never; let y: number; // 运行错误,数字类型不能转为 never 类型 x = 123; // 运行正确,never 类型可以赋值给 never类型 x = (() => { throw new Error('exception') })(); // 运行正确,never 类型可以赋值给 数字类型 y = (() => { throw new Error('exception') })(); // 返回值为 never 的函数可以是抛出异常的情况 function error(message: string): never { throw new Error(message); } // 返回值为 never 的函数可以是无限循环这种无法被执行到的终止点的情况 function loop(): never { while (true) { } }
五、类
1.类的写法
2.类的继承
3.属性修饰符(public、protected、private)
4.必传参数和可选参数
//类里面的修饰符:ts里定义属性的时候提供了三种修饰符: /* public:共有 在类里面、子类、类外面都可以访问 protected:保护类型 在类里面,子类里面可以访问,在类外部没法访问 private:私有 在类里面可以访问 ,类外面和子类都不访问 属性如果不加修饰符,默认是public */ class Person { name: string; //必传,属性修饰符这里没写就默认是public,同public name:string; private sex: string; protected age?: number; //age:可有可没有 //namepro:必传,agepro:可传可不传,可选参数必须配置到 参数的最后面 //‘lisi’是namepro的默认值 constructor(namepro: string = 'lisi', sexpro: string = '男', agepro?: number) { this.name = namepro; this.age = agepro; this.sex = sexpro; } run(): void { console.log(this.name); } } let zhangsan = new Person('zhangsan', '女'); zhangsan.run(); zhangsan.age; //提示出错。age是保护类型 class Web extends Person { constructor(name: string) { super(name) //继承父级的参数需要同过super函数传值 } work() { console.log(this.sex); //sex是父类的私有属性,所以子类是访问不到的,这里会提示错误 } }
5.参数中的三点运算符
//三点运算符,接受新参传过来的值 function sumFn(...result: number[]): number { let sum: number = 0; for (let i = 0; i < result.length; i++) { sum += result[i]; } return sum; } sumFn(1, 2, 3, 4); sumFn(1, 2, 3, 4, 5, 6) //如果有默认的参数,那传参前面就是默认的参数,剩下的就在。。。的result中 function sumFn(a:number,b:number,...result: number[]): number { let sum: number = a+b; for (let i = 0; i < result.length; i++) { sum += result[i]; } return sum; } sumFn(1, 2, 3, 4); sumFn(1, 2, 3, 4, 5, 6)
六、ts函数重载
1.ts中的重载:通过为同一个函数提供多个函数类型定义来试下多种功能的目的
function getInfo(name: string): string; function getInfo(age: number): string; function getInfo(str: any): any { if (typeof str === 'string') { return '我叫' + str; } else { return '我的年龄' + str; } } getInfo('张三') //我叫张三 getInfo(20) //我的年龄20
七、类的静态属性 静态方法
1. /*es5中的写法*/
function Person () { // this.run = function () {} // 实例方法 // this.run = ()=> {} } Person.run = function () {} // 静态方法
2./*es6的写法*/
class Person1 { name:string; static sex = 'man'; // 静态属性 constructor(name: string) { this.name = name; } eat() { console.log(`${this.name}吃饭`) } static work() { console.log(`这是一个静态方法` + Person1.sex) // console.log(`${this.name}哈哈`) // 错误的写法,静态方法里面无法调用类的属性、方法。 } } var p = new Person1('aaa'); p.eat(); // 实例方法调用 Person1.work(); // 静态方法调用
八、ts中的抽象类和抽象方法,多态
1.typescript中的抽象类是提供其它类的基类,不能直接被实例化;
2.用abstract关键字定义的抽象方法和抽象类,不包括具体实现必须在派生类实现。
3. 抽象类: abstract 修饰, 里面可以没有抽象方法。但有抽象方法(abstract method)的类必须声明为抽象类(abstract class)
4.抽象类用于定义标准
5. 多态:父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现
abstract class Animal { name: string; constructor(name: string) { this.name = name; } //抽象方法 ,不包含具体实现,要求子类中必须实现此方法 abstract eat(): any; //非抽象方法,无需要求子类实现、重写 run(): void { console.log('非抽象方法,不一定要子类实现、重写'); } } //子类中必须实现父类抽象方法,否则ts编译报错 class Dog extends Animal { eat() { return this.name + '吃鱼' } } class Cat extends Animal { //子类中必须实现父类抽象方法,否则ts编译报错 eat() { return this.name + "吃鱼"; } } var dog = new Dog("tom"); var cat = new Cat("kitty"); console.log(dog.eat()); console.log(cat.eat()); //多态 ,一种事物的不同表现形态。如下面的代码中 先声明变量f是Animal类型,具体是Dog还是Cat,在new 对象时才知道 //如果是Dog,则f.eat()调用的是Dog类中的eat方法;如果是Cat,则f.eat()调用的是Cat类中的eat方法,这就是多态!!! var f: Animal;//声明变量为Animal类型 //f=new Dog("sunny"); f = new Cat("sunny"); console.log(f.eat());
九、接口
1.接口:行为和动作的规范,对批量方法进行约束
2.接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面
3.分类:属性接口、函数类型接口、可索引接口、类类型接口
(1)属性接口:
//属性接口 interface fullName{ firstName:string; secondName:string; } function getName(name:fullName):void{ console.log(name.firstName + name.secondName); } getName({ firstName:'Li', secondName:'haha' })
interface Shape { head: string; arm: string; } interface Human { name: string; age: number; shape: Shape; say(word: string): void; } let jack: Human = { name: 'Jack', age: 18, shape: { head: 'head', arm: 'arm' }, say(word: string) { console.log(word) } } jack.say('hi')
(2)函数类型接口:
//函数类型接口 interface Fn{ (key:string,val:string):string; } let add1:Fn = function(key:string,val:string):string{ return key+val; } let add2:Fn = function(key:string,val:string){ return `${key}------${val}` }
(3)可索引接口(数组对象的约束,不常用)
//ts定义数组的方式 let arr1: number[] = [11, 22]; let arr2: Array<string> = ['11', '22']; //可索引接口的实现 interface UserArr { [index: number]: string } let arr3: UserArr = ['aa', 'bb'] //ok console.log(arr3[0]); let arr4:UserArr=[11,'aa'];//报错 interface UserObj{ [index:string]:string } let arr5:UserObj ={name:'张三'} //OK,但是这样实现已经不是数组,没有意义了,所以不常用
(4)类类型接口(对类的约束,和抽象类有点像)
interface Aniaml1 { name: string; eat(str: string): void; } //implements ,是对类Aniaml1的实现 class Dog1 implements Aniaml1 { name: string; constructor(name: string) { this.name = name; } eat(str: string) { console.log(this.name + '吃粮食' + str); } } let d = new Dog1('小黑'); d.eat('肉');
4.接口的拓展:接口可以继承接口
interface Animal { eat(): void; } interface Person extends Animal { work(): void; } class Web implements Person { name: string; constructor(name: string) { this.name = name; } eat() { console.log(this.name + '喜欢吃馒头'); } work() { console.log(this.name + '写代码'); } } let zhangsan = new Web('zhangsan'); zhangsan.work();
十、泛型
1.泛型的定义:指在定义函数、接口或者类的时候, 不预先指定其类型,而是在使用时手动指定其类型的一种特性
2.泛型分类:泛型函数、泛型类、泛型接口
(1)泛型函数
//使用泛型函数(T:类型变量) function myfn<T>(args: T): T { return args; } let output = myfn<string>("Hello"); //明确传入类型参数 let output2 = myfn("Hello") //不传入类型参数,TS根据上下文自动推断类型 //使用泛型变量(这可以让我们把泛型变量T当做类型的一部分使用,而不是整个类型) function myfn1<T>(args: T[]): T[] { console.log(args.length) return args; } function myfn2<T>(args: Array<T>): Array<T> { console.log(args.length); return args; }
(2)泛型类
class add<T>{ value: T; add: (x: T, y: T) => T; } let myTest = new add<number>(); myTest.value = 0; myTest.add = function (x, y) { return x + y }
(3)泛型接口
//第一种方法 interface Test { <T>(args: T): T } let myTest: Test = function <T>(args: T): T { return args; } myTest<string>('20'); myTest<string>(20); //第二种方法 interface Test1<T> { (args: T): T } function myfn1<T>(args: T): T { return args; } let myTest1: Test1<string> = myfn1; myTest1('20') //ok myTest1(20)//报错
(4)泛型约束(类型变量继承接口)
//例中,传入的参数必须具有length属性 interface Test{ length:number; } function myfn<T extends Test>(args:T):T{ console.log(args.length) return args; } myfn(3); //error myfn("abc") //3
十一:模块
1.模块的概念
2.模块导出的几种方式:export导出声明、export导出语句、export default、import 导入模块
3.模块封装(DB库)
4.命名空间
十二、装饰器
1.概念
2.分类:属性装饰器、方法装饰器、方法参数装饰器、类装饰器(装饰器的执行顺序也是这样)
3.装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
(1)普通装饰器
//类装饰器 function logClass(target: any) { console.log(target); //httpClient类 params.prototype.apiUrl = '动态扩展的属性'; params.prototype.run = function () { console.log('动态拓展的方法'); } } @logClass class httpClient { constructor() { } getData() { } } let http: any = new httpClient();
//类装饰器重载构造函数和当前类的方法(普通装饰器) function logClass(target: any) { //target:httpClient 类 return class extends target { //装饰器重载构造函数 apiUrl: any = '我是修改后的数据'; getData() { this.apiUrl + '----'; console.log(this.apiUrl); } } } @logClass class httpClient { public apiUrl: string | undefined; constructor() { this.apiUrl = '我是构造函数里面的apiUrl'; } getData() { console.log(this.apiUrl); } } let http: any = new httpClient(); console.log(http.apiUrl); //我是修改后的数据
(2)装饰器工厂(可传参)
2.1 类装饰器
//类装饰器(装饰器工厂的方式) function logClass(params: any) { //params:传的参数 return function (target: any) {//target:httpClient类 console.log(params);//hello console.log(target);//httpClient类 target.prototype.apiUrl = params; //可以用上传的参数 target.prototype.run = function () { console.log('动态拓展的方法'); } } } @logClass('hello') //可以传参 class httpClient { constructor() { } getData() { } } let http: any = new httpClient(); console.log(http.apiUrl); //hello
2.2 属性装饰器
//属性装饰器 function logProperty(params: any) { //params:传的值:我是属性装饰器 return function (target: any, attr: any) { //target:httpClient 类,attr:apiUrl target[attr] = params; } } class httpClient { @logProperty('我是属性装饰器') //注意后面是不能加分号的 public apiUrl: string | undefined; constructor() { this.apiUrl = '我是构造函数里面的apiUrl'; } getData() { console.log(this.apiUrl); } } let http: any = new httpClient(); console.log(http.apiUrl); //我是属性装饰器
2.3 方法装饰器//方法装饰器
function get(params: any) { //params:传的值:我是方法装饰器 //target:httpClient类,methodName:方法名getData, desc:方法的描述 return function (target: any, methodName: any, desc: any) { target.addPara = '我是拓展的属性'; //同类装饰器的方法 target.addFn = function () { //同类装饰器的方法 console.log('我是拓展的方法'); } //修改装饰器的方法,把装饰器里面传入的参数改为string类型 //获取当前的方法 desc.value =getData() {console.log(this.apiUrl);} //先保存当前的方法 let oldMethond = desc.value; //修改当前的方法 desc.value = function (...args: any[]) { args = args.map((val) => { return String(val) }) console.log(args);//['123','xxx']
console.log(params); //我是方法装饰器 //将原来的方法继承到修改后的方法里面,这样既有修改的方法,又包含原有未修改的方法 oldMethond.apply(this, args) } } } class httpClient { public apiUrl: string | undefined; constructor() { this.apiUrl = '我是构造函数里面的apiUrl'; } @get('我是方法装饰器') getData(...args: any[]) { console.log(args); //['123','xxx'] console.log(this.apiUrl); //我是构造函数里面的apiUrl } } let http: any = new httpClient(); http.getData; //['123','xxx']
//我是方法装饰器
//['123','xxx'] //我是构造函数里面的apiUrl
2.4 方法参数装饰器
//方法参数装饰器:可以为类的原型增加一些元素数据(一般不用,类装饰器就可以做到这些) function logParams(params: any) { //params:传的值:我是方法参数装饰器 //target:httpClient类,methodName:方法名getData, paramsIndex:函数参数的index索引 return function (target: any, methodName: any, paramsIndex: any) { target.addUrl = 'params';//可以为类的原型增加一些元素数据 } } class httpClient { public apiUrl: string | undefined; constructor() { } getData( @logParams('我是方法参数装饰器') uuid:any) { console.log(uuid); //haha } } let http: any = new httpClient(); http.getData('haha');