TypeScript
定位:TypeScript是JavaScript的超集(功能更强大(?),语法更易懂和简洁(?))
运行过程:.ts文件编译成.js文件【TypeScript编译器或Babel转译】
目标:开发大型项目
类型
枚举 Enum
关键字:枚举
枚举类型用于定义数值集合。
1 2 3
| enum Color {Red, Green, Blue}; let c: Color = Color.Blue; console.log(c);
|
元组 Tuple
关键字:无
元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
1 2 3 4
| let x: [string, number]; x = ['Runoob', 1]; x = [1, 'Runoob']; console.log(x[0]);
|
never
关键字:never
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let x: never; let y: number;
x = 123;
x = (()=>{ throw new Error('exception')})();
y = (()=>{ throw new Error('exception')})();
function error(message: string): never { throw new Error(message); }
function loop(): never { while (true) {} }
|
无返回值 void
定义一个方法时,方法没有返回任何值
1 2 3
| function run():void{ console.log('run') }
|
null undefined
1 2
| let num:number | null | undefined
|
任意类型 any
1 2 3 4 5 6
| let oBox:any = document.getElementById('box')
oBox.style.color = 'red'
|
boolean
number
string
array
类 Class
类里面的修饰符
- public 都可访问
- private 自身内部可访问
- protected 自身及子类内部可访问
属性如果不加修饰符,默认就是public抽象类和抽象方法
定义一个标准
- 抽象方法必须在抽象类里面
- 抽象类不能被直接实例化
- 抽象类的子类必须实现抽象类里的抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| abstract class Animal{ name:string; constructor(name:string){ this.name = name } abstract eat():any }
const animal = new Animal()
class dog extends Animal{ constructor(name:string){ super(name) } eat(){ console.log(this.name+'吃粮食') } }
const d = new dog('旺旺') d.eat()
|
多态和继承
- 子类可重写父类中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Animal{ name:string; constructor(name:string){ this.name = name } eat():any{ console.log('吃方法') } }
class dog extends Animal{ constructor(name:string){ super(name) } eat(){ console.log(this.name+'吃粮食') } }
const d = new dog('旺旺') d.eat()
|
接口 Interface
- 接口中所有的属性都不能有实际的值
- 接口只定义对象的结果而不考虑实际值
- 接口中所有方法都是抽象方法(没有方法体)
- 定义类(class)时,可以实现(implements)一个接口
- 接口对类起到限制作用,定义一个标准
约束json
1 2 3 4 5 6 7 8 9
|
interface person = { name:string; age:number; loveStudy?:boolean; [propName:string]:any; }
|
约束方法
1 2 3 4 5 6 7 8 9 10 11
|
interface encrypt{ (key:string,value:string):string; }
const md5:encrypt = function(key:string,value:string):string{ return key+value }
|
可索引接口:数组、对象的约束(不常用)
1 2 3 4 5 6 7 8 9 10 11 12 13
|
interface userArr{ [index:number]:string } const arr:userArr = ['aaa','bbb']
interface userObj{ [index:string]:string } const obj:userObj = {name:'张三'}
|
对类的约束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
interface Animal{ name:string; eat(str:string):void; } class dog implements Animal{ name:tring; constructor(name:string){ this.name = name } eat(){ console.log(this.name+'吃骨头') } }
const d = new Dog('旺旺') d.eat()
class cat implements Animal{ name:tring; constructor(name:string){ this.name = name } eat(food:string){ console.log(this.name+'eat') } } const c = new Cat('小花猫') c.eat('菜')
|
接口扩展
接口可以继承接口1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
interface Animal{ eat():void; } interface People 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 People{ constructor(name:string){ supper(this.name) } eat(){ console.log(this.name+'喜欢吃馒头') } work(){ console.log(this.name+'写代码') } } const w = new Web('小李') w.eat() w.work() w.coding('write typescript')
|
类型 Type
和接口差不多,但是不能重复定义1 2 3 4 5
| type person = {name:string,age:number} const personArr:person[] = [ {name:'张三',age:10}, {name:'李四',age:20} ]
|
其他
交叉类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface IPerson { id: string; age: number; }
interface IWorker { companyId: string; }
type IStaff = IPerson & IWorker;
const staff: IStaff = { id: 'E1006', age: 33, companyId: 'EFT' };
console.dir(staff)
|
在上面示例中,我们首先为 IPerson 和 IWorker 类型定义了不同的成员,然后通过 & 运算符定义了 IStaff 交叉类型,所以该类型同时拥有 IPerson 和 IWorker 这两种类型的成员。
泛型
遇到类型不明确时可以使用泛型
解决类、接口、方法的复用性,以及对不特定数据类型的支持
泛型函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
function fn<T>(a:T):T{ return a }
let res = fn(10) let res2 = fn<string>('hello')
function fn2<T,K>(a:T,b:K):T{ console.log(b) return a }
fn2<number,string>(10,'hello')
|
泛型类
1 2 3 4 5 6 7 8 9
| class MyClass<T>{ name: T; constructor(name:T){ this.name = name } } const mc = new MyClass<string>('孙悟空') const mco = new MyClass<number>(66)
|
泛型接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| interface ConfigFn{ <T>(value:T):T; } const getData:ConfigFn = function<T>(value:T):T{ return value }
getData<string>('aaa') getData<number>(123)
function getData<T>(value:T):T{ return value } const myGetData:ConfigFn<string> = getData; myGetData('aaa')
interface Inter{ length:number }
function fn3<T extends Inter>(a:T):number{ return a.length }
|
编译时自动判断出类型T
类型名字“T”随意
应用举例 - 数据库相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| interface DBI<T>{ add(info:T):boolean; update(info:T,id:number):boolean; delete(id:number):boolean; get(id:number):any[]; }
class MysqlDb<T> implements DBI<T>{ add(info:T):boolean{
} update(info:T,id:number):boolean{ } delete(id:number):boolean{ } get(id:number):any[]{ } }
class MsSqlDb<T> implements DBI<T>{ add(info:T):boolean{ } update(info:T,id:number):boolean{ } delete(id:number):boolean{ } get(id:number):any[]{ const list = [ {title:'11',desc:'11'}, {title:'111',desc:'111'}, ] return list } }
class User{ username:string | undefined; password:string | undefined; } const u = new User() u.username = 'zhangsan' u.password = 'zhangsan' const oMysql = new MysqlDb<User>() oMysql.add(u)
const oMsql = new MsSqlDb<User>() oMsql.add(u)
|
模块和命名空间
命名空间:内部模块,组织代码,避免命名冲突
一个模块可以包含多个命名空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| namespace A{ interface Animal{ name:string; eat(str:string):void; } export class dog implements Animal{ name:tring; constructor(name:string){ this.name = name } eat(){ console.log(this.name+'吃骨头') } } }
const d = new A.Dog('旺旺') d.eat()
export namespace A{
} import {A} from '...' const d = new A.Dog('旺旺') d.eat()
|
装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法。
常见装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器。
装饰器的写法:普通装饰器(无法传参),装饰器工厂(可传参)。
类装饰器
用于类构造函数,可以用来监视,修改或替换类定义,传入一个参数。
普通装饰器(无法传参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function logClass(params:any){ console.log(params) params.prototype.apiUrl = 'xxx' params.prototype.run = function(){ console.log('run') } }
@logClass class HttpClient{ constructor(){ } getData(){ } }
const http = new HttpClient(); console.log(http.apiUrl) http.run()
|
装饰器工厂(可传参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function logClass(params:string){ return function(target:any){ console.log(target) console.log(params) target.prototype.apiUrl = params target.prototype.run = function(){ console.log('run') } } }
@logClass('http://www.xxx.com/api') class HttpClient{ constructor(){ } getData(){ } }
const http = new HttpClient(); console.log(http.apiUrl) http.run()
|
修改类的构造函数和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function logClass(target:any){ console.log(target) return class extends target{ apiUrl:any = '我是修改后的数据'; getData(){ this.apiUrl = this.apiUrl+'---' console.log(this.apiUrl) } } }
@logClass class HttpClient{ apiUrl:string | undefined; constructor(){ this.apiUrl = '我是构造函数里面的apiUrl' } getData(){ console.log(this.apiUrl) } }
const http = new HttpClient(); console.log(http.apiUrl) http.getData()
|
属性装饰器
给属性赋值…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function logClass(params:string){ return function(target:any){ console.log(target) console.log(params) target.prototype.apiUrl = params target.prototype.run = function(){ console.log('run') } } }
function logProperty(params:any){ console.log(target) return function(target:any,attr:any){ console.log(params) console.log(target) console.log(attr) target[attr] = params } }
@logClass('xxx') class HttpClient{ @logProperty('http://www.xxx.com/api') url:string | undefined; constructor(){ this.apiUrl = '我是构造函数里面的apiUrl' } getData(){ console.log(this.url) } }
|
方法装饰器
修改当前实例的属性和方法,也可以的替换当前被装饰方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function logMethod(params:any){ return function(target:any,methodName:any,desc:any){ console.log(params) console.log(target) console.log(methodName) console.log(desc.value) const oMethod = desc.value desc.value = function(...args:any[]){ args.map(value=>{ return String(value) }) console.log(args) oMethod.apply(this,args) } } } class HttpClient{ url:string | undefined; constructor(){ } @get('http://www.xxx.com/api') getData(){ console.log(this.url) } }
const http:any = new HttpClient(); http.getData(123,'xxx')
|
方法参数装饰器
扩展以及修改当前类的属性及方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function logParams(params:any){ return function(target:any,methodName:any,paramsIndex:any){ console.log(params) console.log(target) console.log(methodName) console.log(paramsIndex) target.apiUrl = params } }
class HttpClient{ url:string | undefined; constructor(){ } getData(@logParams('xxx') uuid:any){ console.log(this.url) } }
const http:any = new HttpClient(); http.getData(123456) console.log(http.apiUrl)
|
装饰器执行顺序
属性 -> 方法 -> 方法参数 -> 类
如果有多个同种装饰器,从后往前执行
结合Vue3
reactive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <script setup lang='ts'> import { reactive } from 'vue' interface User{ name:string; age:number; getInfo():void; } // 方法一 const u:User = reactive({ name:'zhangsan', age:18, getInfo(){ console.log(this.name+this.age+'岁了') } }) // 方法二 const u = reactive<User>({ name:'zhangsan', age:18, getInfo(){ console.log(this.name+this.age+'岁了') } }) // 方法三 const u = reactive({ name:'zhangsan', age:18, getInfo(){ console.log(this.name+this.age+'岁了') } }) as User </script>
|
ref
1 2 3 4 5
| <script setup lang='ts'> import { ref } from 'vue' let u = ref<number|string>(20) u.value = '20' </script>
|
computed
1 2 3 4 5 6 7
| <script setup lang='ts'> import { computed } from 'vue' const username = 'zhangsan' let reverseUsername = computed(():string=>{ return username.split('').reverse().join('') }) </script>
|