我是看视频学的,最下方有b站视频链接
一.安装
npm install -g typescript
cd typescript-demo
tsc --init // 生成tsconfig.json
1、vscode配置自动编译
1.第一步 tsc --inti 生成tsconfig.json 改 "outDir": "./js",
2.第二步 任务 - 运行任务 监视tsconfig.json
二.数据类型
格式一:
let 变量名: 变量类型 = 初始化值
格式二:
let 变量名: 变量类型 | undefined;
变量名 = 变量值;
//1.字符串
const title:string = 'xiaoli'
//2.数字
const age:number = 21
//3.布尔
const flag:boolean = true
//4.数组
const arr:number[] = [1,2,3,4]
//或者
const arr1:Array<number> = [1,2,3,3]
//7.null null 和undefined 是所有类型的子类型 可以把null 和 undefined 赋值给number类型的变量
let n:null = null
//8.undefined 使用strictnullChecks标记,null和undefined只能赋值给void和它们各自
let u:undefined = undefined
//9.any 任意类型
let num:any = 123
let str:any = 'nihao'
let un:any = undefined
//用法:
var oBox = document.getElementById('box');
oBox.style.color = 'red';
//10.void 表示没有任何类型,一般用于定义方法的时候没有返回值
function success(): void {
console.log('执行成功了,我不需要返回值');
}
//11.never 是任何类型的子类型,也可以直接赋值给任何类型,但是没有类型是never的子类型或可以赋值给never类型,即使any类型也不可以赋值给never,这就意味着声明never了类型的变量只能被never类型所赋值
function error(): never {
throw new Error('抛出错误了');
}
//12.组合类型 支持一个变量可以赋予多种不同的变量类型,多个变量类型使用|分隔
let text:number | null | undefined = 23;
-
5.元组
属于数组的一种,按着定义类型的顺序
const arr:[number,string]=[123,'this is ts'];
-
6.枚举
enum 枚举名 { 标识符[= 整型常数/字符串], 标识符[= 整型常数/字符串], ... 标识符[= 整型常数/字符串], };
-
1.如果标识符没有赋值,它的值就是下标
enum Flag { age, name } let c:Flag = Flag.age console.log(c); //0
-
2.如果标识符已经赋值,它的值就是被赋的值
enum Flag { success = 1, error = 0 } const s: Flag = Flag.error console.log(s); //0
-
3.如果标识符没有赋值,它的值就是下标,如果从中间突然指定了一个值,那么它之后的值都会从当前值开始重新计算。
enum Flag { age= 100, name } let c:Flag = Flag.name console.log(c); //101
-
三.函数
格式一:
function 函数名(参数列表): 返回值类型 {
函数体 ...
[return 返回值;]
}
格式二:
let 函数名 = function (参数列表): 返回值类型 {
函数体 ...
[return 返回值;]
};
function run():string{
return 'run';
}
// 错误写法
function run():string{
return 123;
}
1.必选参数
必选参数:在调用函数的时候,必须要传入的参数,参数列表里边的参数默认就是必选参数,只要在声明的时候写了参数,在传递的时候,就必须传入参数,而且,实参与形参的数量与类型要一致。
function getInfo(name:string,age:number):string {
return `${name}---${age}`
}
getInfo('张三',28)//正确
getInfo('张三') //错误
2.可选参数
必选参数:为了解决在函数传参的时候,某些参数可以不用传递,我们就需要可选参数了。
注意:可选参数必须配置到参数的最后面。
function getInfo(name:string,age?:number):string {
return `${name}---${age}`
}
getInfo('张三')
3.默认参数
默认参数:为了解决在函数传参的时候,某些参数可以不用传递,但是我们又需要该参数的值,这时候我们就需要给这个参数设定一个默认值也叫初始化值,就得用到默认参数了。
注意:可选参数不能够进行初始化值的设定。
function getInfo(name:string='王五',age?:number):string {
return `${name}---${age}`
}
4.剩余参数
剩余参数:在参数的类型确定而参数个数不确定的情况时,我们需要用到剩余参数,它使用
...
将接收到的参数传到一个指定类型的数组中。注意:剩余参数必须配置到参数的最后面。
function sum(init:number,...result:number[]):number {
let sum = init;
for (let i = 0; i < result.length; i++) {
sum += result[i];
}
return sum;
}
let num:number = sum(100,1,2,3,4)
5.重载
重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。
TypeScript 中的重载是通过为同一个函数提供多个函数类型声明来实现函数重载的功能的。
function getInfo(name:string):string;
function getInfo(age:number):string;
function getInfo(str:any):any{
if(typeof str==='string'){
return '我叫:'+str;
}else{
return '我的年龄是'+str;
}
}
console.log(getInfo('张三')); //正确
console.log(getInfo(20)); //正确
console.log(getInfo(true)); //错误写法 只支持string和number类型
6.箭头函数
setTimeout(()=>{
alert('run')
},1000)
四.ES5的类
1.构造函数创建类
function Person() {
this.name = 'zhang'
this.age = 20
this.run = function() { //实例方法 需要new
console.log('run');
}
}
var p = new Person()
p.run()
console.log(p.name);
2.添加在原型链上
Person.prototype.sex="男";
Person.prototype.work=function(){ // 原型方法
alert(this.name+'在工作');
}
注意: 原型链上的属性是多个实例共享的,构造函数不共享
var p1 = new Person()
var p2 = new Person()
console.log(p1.run == p2.run) //false 实例方法地址是不同的
console.log(p1.work == p2.work) //true 原型方法地址是相同的
3.静态方法
Person.getInfo = function(){
console.log('静态方法');
}
Person.getInfo()
4.类的继承
-
1.对象冒充实现继承(构造 可 原型 不可)
-
1.可以继承构造函数中的属性和方法
-
2.不可以继承原型链上的属性和方法
function Web() { Person.call(this) } var w = new Web() console.log(w.name); //'zhang' console.log(w.sex); //undefined
-
-
2.原型链实现继承(都可)
- 可以继承构造函数中的属性和方法
- 可以继承原型链上的属性和方法
function Web() { } Web.prototype = new Person() var w = new Web() console.log(w.name); //'zhang' console.log(w.sex); //'男'
-
问题:实例化子类不能给父类传参
function Person(name,age) { this.name = name this.age = age } Person.prototype.sex="男"; Person.prototype.work=function(){ alert(this.name+'在工作'); } var p = new Person('王',20) console.log(p.name); function Web() { } Web.prototype = new Person() var w = new Web('张',30) console.log(w.name); //undefined
-
3.对象冒充 + 原型链 组合继承
function Web(name,age) { Person.call(this,name,age) } Web.prototype = new Person() var w = new Web('张',30) console.log(w.name); //'张'
-
4.组合继承的另一种方式
function Web(name,age) { Person.call(this,name,age) } Web.prototype = Person.prototype var w = new Web('张',30) console.log(w.name); //'张'
五.TS中的类
class Person {
name:string; //属性,前面省略了public关键字
constructor(n:string) { //构造函数,实例化类的时候触发的方法
this.name = n //使用this关键字为当前类的name属性赋值
}
run():void { //方法
console.log(this.name);
}
work() {
console.log(`父类名字:${this.name}`);
}
}
var p = new Person('张三')
p.run()
class Person {
name:string;
constructor(name:string) {
this.name = name
}
getName():string {
return this.name
}
setName(name:string):void {
this.name = name
}
}
var p = new Person('张三')
console.log(p.getName()); //'张三'
p.setName('王五')
console.log(p.name); // '王五'
1.继承(extends和super)
class Web extends Person {
constructor(name:string) {
super(name) // 初始化父类的构造函数,传递给Person的constructor
}
}
var w = new Person('张')
console.log(w.name); //张
问题: 当父类和子类有同名的方法,只调用子类中的方法
class Web extends Person {
constructor(name:string) {
super(name) // 传递给Person的constructor
}
work() {
console.log(`子类名字:${this.name}`);
}
}
var w = new Web('张')
w.work()
2.修饰符(public、proteced、private)
- public : 公有,在类里面、子类或者类外边可以访问(默认)
class Person {
public name:string;
constructor(name:string) {
this.name = name
}
getName():string {
return this.name //类里面访问
}
setName(name:string):void {
this.name = name
}
work() {
console.log(`父类名字:${this.name}`);
}
}
var p = new Person('张三')
console.log('名字'+p.getName());//可以访问到
console.log(p.name); //类外边访问
//子类
class Web extends Person {
constructor(name:string) {
super(name)
}
work() {
console.log(`子类名字:${this.name}`); // 子类访问
}
}
var w = new Web('张')
w.work()
- protected :保护类型,在类里面和子类可以访问,在类外边访问不了
class Person {
protected name:string;
constructor(name:string) {
this.name = name
}
getName():string {
return this.name //类里面
}
setName(name:string):void {
this.name = name
}
work() {
console.log(`父类名字:${this.name}`);
}
}
var p = new Person('张三')
console.log('名字'+p.getName());
console.log(p.name); //name报错 属性“name”受保护,只能在类“Person”及其子类中访问 结果是能打印出来
class Web extends Person {
constructor(name:string) {
super(name)
}
work() {
console.log(`子类名字:${this.name}`); //子类
}
}
var w = new Web('张')
w.work()
- private:私有,在类里面可以访问,在子类和类外边访问不了
class Person {
private name:string;
constructor(name:string) {
this.name = name
}
getName():string {
return this.name //类里面
}
setName(name:string):void {
this.name = name
}
work() {
console.log(`父类名字:${this.name}`);
}
}
var p = new Person('张三')
console.log('名字'+p.getName());
console.log(p.name); //name报错 属性“name”为私有属性,只能在类“Person”中访问
class Web extends Person {
constructor(name:string) {
super(name)
}
work() {
console.log(`子类名字:${this.name}`); //name报错 属性“name”为私有属性,只能在类“Person”中访问
}
}
var w = new Web('张')
w.work()
3.静态属性和静态方法
- ES中的静态属性和静态方法
function Person() {
this.work = function() {
}
}
Person.name = '小明' //静态属性
Person.run = function() {
console.log('静态方法');
}
Person.run() //调用静态方法
- TS中的静态属性和静态方法(static)
class Person {
name:string;
age:number = 20
static sex='男'
constructor(name:string) {
this.name = name
}
static run() { //静态方法 可以与实例方法同名
console.log('静态方法');
}
run() {
console.log('run'); //实例方法 需要实例化 new
}
}
console.log(Person.sex);
Person.run()
4.多态
- 定义:父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。多态属于继承
class Animal {
name:string
constructor(name:string) {
this.name = name
}
eat() { //父类方法
}
}
class Dog extends Animal {
constructor(name:string) {
super(name)
}
eat() { //子类实现
return `${this.name}吃骨头`
}
}
var d = new Dog('小狗')
console.log(d.eat());
class Cat extends Animal {
constructor(name:string) {
super(name)
}
eat() { //子类实现
return `${this.name}吃鱼`
}
}
var t= new Cat('小猫')
console.log(t.eat());
5.抽象类(abstract)
-
定义:提供其他类继承的基类(父类),不能直接实例化。
-
使用:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现,abstract抽象方法只能在抽象类中,抽象类和抽象方法是用来定义标准的。
//标准:Animal 这个类要求它的子类必须包含eat方法
abstract class Animal { //抽象类
name:string
constructor(name:string) {
this.name = name
}
abstract eat():any //抽象方法不包含具体实现并且必须在派生类中实现
}
//报错:无法创建抽象类的实例。
var a = new Animal() //错误的写法 抽象类不能直接实例化
class Dog extends Animal {
constructor(name:string) {
super(name)
}
//子类中没有eat方法会报错:非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“eat”
eat() { //抽象类的子类必须实现抽象类里面的抽象方法
return `${this.name}吃骨头`
}
}
var d = new Dog('小狗')
console.log(d.eat());
class Cat extends Animal {
constructor(name:string) {
super(name)
}
eat() { 抽象类的子类必须实现抽象类里面的抽象方法
return `${this.name}吃鱼`
}
}
var t= new Cat('小猫')
console.log( t.eat());
六.接口(interface)
- 作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。 typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
1.属性接口
- 约束:对json约束
interface FullName {
firstName:string;
lastName:string;
}
function printLabel(labelInfo:FullName):void {
console.log('labelInfo.firstName+'---'+labelInfo.lastName')
}
//调用方法一种方式 只能传入firstName和lastName属性
printLabel({
//报错:类型“{ name: string; firstName: string; lastName: string; }”的参数不能赋给类型“FullName”的参数。 对象文字可以只指定已知属性,并且“name”不在类型“FullName”中。
name:'123',
firstName:'小明',
lastName:'小红'
})
printLabel({ //不要求参数顺序
firstName:'小明',
lastName:'小红'
})
//另一种方式 传入的参数必须包含 firstName secondName
let obj = {
name:'123',
firstName:'小明',
lastName:'小红'
}
printLabel(obj)
- 可选参数
interface FullName {
firstName:string;
lastName?:string; //加? lastName为可选参数
}
function printLabel(labelInfo:FullName):void {
console.log(labelInfo);
}
printLabel({
firstName:'小明',
})
- 原生js封装ajax
interface Config {
type:string;
url:string;
data?:string;
dataType:string;
}
function ajax(config:Config) {
var xhr = new XMLHttpRequest()
xhr.open(config.type,config.url,true)
xhr.send(config.data)
xhr.onreadystatechange = function() {
if(xhr.readyState==4&&xhr.status==200) {
console.log('成功');
if(config.dataType=='json') {
console.log(JSON.parse(xhr.responseText));
} else {
console.log(xhr.responseText);
}
}
}
}
ajax({
type:'get',
url:'http://a.itying.com/api/productlist',
dataType:'json'
})
2.函数接口
- 约束:对于方法传入参数和方法返回值进行约束,也可以批量约束
interface encrypt {
(type:string,value:string):string;
}
//传入的参数和返回值都应是string
var md5:encrypt = function(type:string,value:string):string {
return type + value
}
//错误:不能将类型“(type: string, value: number) => string”分配给类型“encrypt”。参数“value”和“value” 的类型不兼容,不能将类型“string”分配给类型“number”。
var md5:encrypt = function(type:string,value:number):string {
return type + value
}
var sha1:encrypt = function(type:string,value:string):string {
return `${type}:${value}`
}
console.log(md5('名字是','小张'));
console.log(sha1('名字是','小王'));
3.可索引接口(不常用)
-
约束:数组、对象约束
-
对数组约束
interface UseArr {
[index:number]:string
}
//数组中的每一项是string
var ar:UseArr = ['小王','小张']
var ar:UseArr = [20,'小张'] //错误写法
- 对对象约束
interface UseObj {
[index:string]:string;
}
var obj:UseObj = {
name:'小王',
}
4.类类型接口
- 约束:对类的约束,与抽象类相似
interface Animal {
name:string;
eat(name:string):string
}
//implements 实现 必须有name属性和eat方法
class Dog implements Animal {
name:string
constructor(name:string) {
this.name = name
}
eat() {
return `${this.name}吃骨头`
}
}
var d = new Dog('小狗')
console.log(d.eat());
class Cat implements Animal {
name:string
constructor(name:string) {
this.name = name
}
eat() {
return `${this.name}吃鱼`
}
}
var ca = new Cat('小猫')
console.log(ca.eat());
5.类接口扩展
- 接口可以继承接口
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);
}
}
// Person的实现需要包含eat和work的方法
class Web extends Programmer implements Person {
constructor(name:string) {
super(name)
}
eat(){
}
work() {
}
}
var w = new Web('小李')
w.coding('敲代码')
七.泛型
1.定义
- 定义:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
- 通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
2.泛型函数
//只能返回string类型的数据
function getData(value:string):string{
return value;
}
//同时返回 string类型 和number类型 (代码冗余)
function getData1(value:string):string{
return value;
}
function getData2(value:number):number{
return value;
}
//同时返回 string类型 和number类型 any可以解决这个问题
function getData(value:any):any{
return '哈哈哈';
}
getData(123);
getData('str');
//any放弃了类型检查,传入什么 返回什么。比如:传入number 类型必须返回number类型 传入 string类型必须返回string类型
//传入的参数类型和返回的参数类型可以不一致
function getData(value:any):any{
return '哈哈哈';
}
//泛型:可以支持不特定的数据类型 要求:传入的参数和返回的参数一致
// T表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value:T):T {
return value
}
getData<number>(123)
getData<number>('123')//错误写法 类型“string”的参数不能赋给类型“number”的参数
getData<string>('12223')
getData<string>(123) //错误写法 类型“number”的参数不能赋给类型“string”的参数
function getData<T>(value:T):any{
return '1231331'
}
getData<string>('这是一个泛型')
getData<number>(1231)
3.泛型类
//比如有个最小堆算法,需要同时支持返回数字和字符串 a - z两种类型。 通过类的泛型来实现
//只支持一种类型 number
class MinClass {
public list:number[]= []
add(num:number) {
this.list.push(num)
}
min():number{
var minNum = this.list[0]
for(var i=0;i< this.list.length;i++) {
if(minNum>this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
var m = new MinClass()
m.add(2)
m.add(4)
m.add(6)
m.add(1)
console.log(m.min()); //1
//类的泛型
class MinClass<T> {
public list:T[]= []
add(value:T) {
this.list.push(value)
}
min():T{
var minNum = this.list[0]
for(var i=0;i< this.list.length;i++) {
if(minNum>this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
var m = new MinClass<number>() //实例化类 并且制定了类的T代表的类型是number
m.add(2)
m.add(4)
m.add(6)
m.add(1)
m.add(2)
console.log(m.min());//1
var ms = new MinClass<string>() //实例化类 并且制定了类的T代表的类型是string
ms.add('a')
ms.add('c')
ms.add('d')
ms.add('f')
ms.add('k')
console.log(ms.min()); //‘a’
4.泛型接口
//函数接口
interface ConfigFn {
(value1:string,value2:string):string
}
var setData:ConfigFn = function(value1:string,value2:string):string {
return value1+value2
}
console.log(setData('name','张三'));
//泛型接口
interface ConfigFn {
<T>(value:T):T
}
var setData:ConfigFn = function<T>(value:T):T {
return value
}
setData<string>(233) //错误写法
console.log(setData<string>('张三'));
//另一种写法
interface ConfigFn<T> {
(value:T):T
}
function setData<T>(value:T):T {
return value
}
var getData:ConfigFn<string>=setData
getData('张三')
getData(123) //错误写法
5.泛型类扩展
/*
定义一个User的类这个类的作用就是映射数据库字段
然后定义一个 MysqlDb的类这个类用于操作数据库
然后把User类作为参数传入到MysqlDb中
*/
// 把类作为参数来约束数据传入的类型 添加用户信息
class User {
username:string | undefined;
password:string | undefined
}
class MysqlDb {
add(info:User):boolean {
console.log(info);
return true
}
}
var u = new User()
u.username ='张三'
u.password = '12333'
var m = new MysqlDb()
m.add(u)
//添加文章内容
class ArticalCat {
title:string | undefined;
desc:string | undefined;
status:number|undefined
}
class MysqlDb {
add(info:ArticalCat):boolean {
console.log(info);
return true
}
}
var a = new ArticalCat()
a.title = '标题'
a.desc = '描述'
a.status = 0
var m = new MysqlDb()
m.add(a)
// 问题:代码重复 都是调用MysqlCat中的add方法,只是传入的参数不同
class User {
username:string | undefined;
password:string | undefined;
}
var u = new User()
u.username ='admin'
u.password='123'
class MysqlDb<T> { // 可以复用
add(info:T):boolean {
console.log(info)
return true
}
updated(info:T,id:number):boolean {
console.log(info);
return true;
}
}
var m = new MysqlDb<User>()
m.add(u)
class ArticleCate {
title:string | undefined;
desc:string | undefined;
status?:number | undefined;
constructor(params:{
title:string | undefined,
desc:string | undefined,
status?:number | undefined,
}) {
this.title = params.title
this.desc = params.desc
this.status = params.status
}
}
var a = new ArticleCate({
title:'标题',
desc:'描述',
status:0
})
var db = new MysqlDb<ArticleCate>()
db.add(a)
// 修改数据
var e = new ArticleCate({
title:'修改标题',
desc:'修改描述',
})
e.status = 1
db.update(e,12)
6.综合使用
功能:定义一个操作数据库的库 支持 Mysql Mssql MongoDb
要求1:Mysql MsSql MongoDb功能一样 都有 add update delete get方法
注意:约束统一的规范、以及代码重用
解决方案:需要约束规范所以要定义接口 ,需要代码重用所以用到泛型
1、接口:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范
2、泛型 通俗理解:泛型就是解决 类 接口 方法的复用性、
//定义泛型接口
interface DBI<T> {
add(info:T):boolean;
get(id:number):any[];
update(info:T):boolean;
delete(id:number):boolean;
}
//定义一个操作mysql数据库的类 注意:要实现泛型接口 这个类也应该是一个泛型类
class MysqlDb<T> implements DBI<T> {
add(info: T): boolean {
console.log(info);
return true
}
get(id: number): any[] {
var list=[
{
title:'xxxx',
desc:'xxxxxxxxxx'
},
{
title:'xxxx',
desc:'xxxxxxxxxx'
}
]
return list;
}
update(info: T): boolean {
throw new Error("Method not implemented.")
}
delete(id: number): boolean {
throw new Error("Method not implemented.")
}
}
//定义一个操作mssql数据库的类
class MsSql<T> implements DBI<T> {
add(info: T): boolean {
console.log(info);
return true
}
get(id: number): any[] {
var list=[
{
title:'xxxx',
desc:'xxxxxxxxxx'
},
{
title:'xxxx',
desc:'xxxxxxxxxx'
}
]
return list;
}
update(info: T): boolean {
throw new Error("Method not implemented.")
}
delete(id: number): boolean {
throw new Error("Method not implemented.")
}
}
//操作用户表 定义一个User类和数据表做映射
class User {
username:string | undefined;
password:string | undefined;
}
var u = new User()
u.username='admin'
u.password ='123'
//将数据传递数据库只需要new数据库的实例并调用对象方法
var mysql = new MysqlDb<User>()
mysql.add(u)
var data = mysql.get(2)
console.log(data);
var mssql = new MsSql<User>()
mssql.add(u)
八.模块化
/modules/db.ts
var dbUrl = 'xxxxxxxx'
export function getData() {
console.log('数据库连接成功');
}
interface DBI<T> {
add(info:T):boolean;
get(id:number):any[];
update(info:T):boolean;
delete(id:number):boolean;
}
export class MysqlDb<T> implements DBI<T> {
add(info: T): boolean {
console.log(info);
return true
}
get(id: number): any[] {
var list=[
{
title:'xxxx',
desc:'xxxxxxxxxx'
},
{
title:'xxxx',
desc:'xxxxxxxxxx'
}
]
return list;
}
update(info: T): boolean {
throw new Error("Method not implemented.")
}
delete(id: number): boolean {
throw new Error("Method not implemented.")
}
}
/model/user.ts
import {MysqlDb} from '../modules/db'
class UserClass {
username:string | undefined;
password:string | undefined;
}
var userModel = new MysqlDb<UserClass>()
export {
UserClass,
userModel
}
/model/article.ts
import {MysqlDb} from '../modules/db'
class ArticleClass {
title:string | undefined;
desc:string | undefined;
}
var ArticleModel = new MysqlDb<ArticleClass>()
export {
ArticleClass,
ArticleModel
}
//index.ts
import { UserClass,userModel } from "./model/user";
import { ArticleClass,ArticleModel } from "./model/article";
// 增加数据
var u = new UserClass()
u.username = 'admin'
u.password='123'
userModel.add(u)
var a = new ArticleClass()
a.title='标题'
a.desc='描述'
ArticleModel.add(a)
九.命名空间(namespace)
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间,同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。命名空间内的对象通过export关键字对外露。
-
命名空间和模块的区别:
-
命名空间:内部模块,主要用于组织代码,避免命名冲突。
-
模 块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
-
namespace A{
interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃狗粮。`);
}
}
export class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃猫粮。`);
}
}
}
namespace B{
interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃狗粮。`);
}
}
export class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃猫粮。`);
}
}
}
var c=new B.Cat('小花');
c.eat();
//index.ts
import {A,B} from './modules/animal';
var d=new A.Dog('小黑');
d.eat();
var dog=new B.Dog('小花');
dog.eat();
十.装饰器
1.定义
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。 装饰器是过去几年中js最大的成就之一,已是Es7的标准特性之一
-
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
-
装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参)
2.类装饰器
2.1定义
类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 传入一个参数
//用法:
function logClass(params:any) { //装饰器
console.log(params); //拿到的是类 class HttpClient {}
}
@logClass // 用在类的上边
class HttpClient {
}
2.2.普通装饰器(无法传参)
function logClass(params:any) {
console.log(params);
params.prototype.apiUrl = 'xxxxx'
params.prototype.run = function() {
console.log('我是一个run方法');
}
}
@logClass
class HttpClient {
}
var h:any = new HttpClient() //这里不加:any会报错:类型“HttpClient”上不存在属性“apiUrl”
console.log(h.apiUrl); //'xxxxx'
h.run()
2.3.装饰器工厂(可传参)
function logClass(params:any) {
return function(target:any) {
console.log(target);//'http://baidu.com'
console.log(params); //class HttpClient {}
target.prototype.apiUrl = params
}
}
@logClass('http://baidu.com')
class HttpClient {
}
var h:any = new HttpClient()
console.log(h.apiUrl);
2.4.例子
下面是一个重载构造函数的例子。
-
类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
-
如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
function logClass(target:any) {
console.log(target);
return class extends target { // 修改构造函数 和方法
apiUrl:any = '修改后的apiUrl'
getData() { // getData不在这里写logClass会报错
console.log(this.apiUrl+'-----------');
}
}
}
@logClass
class HttpClient {
public apiUrl:string | undefined;
constructor() {
this.apiUrl = '我是构造函数里面的apiUrl'
}
getData() {
console.log(this.apiUrl);
}
}
var h:any = new HttpClient()
h.getData()
3.属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2.成员的名字。
function logProperty(params:any) {
return function(target:any,arr:any) {
console.log(target);//构造函数
console.log(arr); //url
target[arr] = params
}
}
class HttpClient {
@logProperty('xxxxxx')
public url:string | undefined;
}
var h:any = new HttpClient()
console.log(h.url);//xxxxxx
4.方法装饰器
它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰会在运行时传入下列3个参数:
1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2.成员的名字。
3.成员的属性描述符。
function logMethod(params:any) {
console.log(params);
return function (target:any,methodName:any,desc:any) {
console.log(target);//1.实例对象
console.log(methodName); //2.方法名字
console.log(desc.value); // 3.ƒ getData() {console.log('getData方法'); }
target.url = params
}
}
class HttpClient {
public url:string | undefined
@logMethod('xxxxxxx')
getData() {
console.log('getData方法');
}
}
var h = new HttpClient()
h.getData()
//修改getData中的数据
function logMethod(params:any) {
console.log(params);
return function (target:any,methodName:any,desc:any) {
console.log(target);//实例对象
console.log(methodName); //方法名字
console.log(desc.value); // ƒ getData() {console.log('getData方法'); }
var oMethod = desc.value
desc.value = function(...args:any[]) {
args = args.map(item=>{
return String(item) //将参数变为字符串
})
oMethod.apply(this,args)
}
}
}
class HttpClient {
public url:string | undefined
@logMethod('xxxxxxx') //引入装饰器
getData(...args:any[]) {
console.log(args);
}
}
var h = new HttpClient()
h.getData(1313,33434)
5.方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2.参数的名字。
3.参数在函数参数列表中的索引。
function logParams(params:any) {
return function(target:any,methodName:any,paramsIndex:any) {
console.log(target);
console.log(methodName);
console.log(paramsIndex);
console.log(params);
}
}
class HttpClient {
getData(@logParams('xxxxx') uid:any) {
console.log(uid);
}
}
var h:any = new HttpClient()
h.getData('1213')
6.装饰器的执行顺序
执行顺序: 属性>方法>方法参数>类
如果有多个同样的装饰器,它会先执行后面的
function logClass1(params:string){
return function(target:any){
console.log('类装饰器1')
}
}
function logClass2(params:string){
return function(target:any){
console.log('类装饰器2')
}
}
function logAttribute1(params?:string){
return function(target:any,attrName:any){
console.log('属性装饰器1')
}
}
function logAttribute2(params?:string){
return function(target:any,attrName:any){
console.log('属性装饰器2')
}
}
function logMethod1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法装饰器1')
}
}
function logMethod2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法装饰器2')
}
}
function logParams1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法参数装饰器1')
}
}
function logParams2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法参数装饰器2')
}
}
@logClass1('http://www.itying.com/api')
@logClass2('xxxx')
class HttpClient{
@logAttribute1()
@logAttribute2()
public apiUrl:string | undefined;
constructor(){
}
@logMethod1()
@logMethod2()
getData(){
return true;
}
setData(@logParams1() attr1:any,@logParams2() attr2:any,){
}
}
var http:any=new HttpClient();
参考:
1.学习TypeScript4这一篇就够了
2.b站视频(Typescript教程_Typescript视频教程 ts入门实战视频教程-2021年更新 包含Vue3+Ts)