TypeScript基础入门笔记

// // 定义string
// let str:string = '你好啊哈'

// // 定义boolean
// let flag:boolean = true
// // flag = 123  报错因为上方定义了此变量只能为boolean类型


// // 定义numbur
// let age:number = 18
// age = 18.1  // 在ts里面没有区分整形和浮点型
// // console.log(age)


// // 定义数组(有两种定义方法,定义时可以统一指定数组内元素的类型)
// let arr2:number[] = [1,2,3]
// let arr1:Array<string> = ['js','python']  // 使用泛型


// // 定义元组(元组属于数组的变种,编译成es5之后其实就是数组,区别就是元组可以按顺序自定义数组中每个元素的类型)
// let tuple1:[string,number] = ['python',123]
// // tuple1 = [123,'小明']  // 报错,因为没有按照声明的顺序
// // console.log(tuple1)


// // 定义枚举类型enum(默认使用元素的下标当做枚举的值,可以手动指定下标的起始位置,当然我们也可以手动指定内部元素对应的值而不使用下标)
// enum color1 {red,green,blue}  // 使用默认的下标
// let red:color1 =  color1.red
// console.log(red)
// enum color2 {red=100,green,blue}  // 指定初始元素的下标
// let green = color2.green
// console.log(green)
// enum color3 {red,green=5,blue}  // 此时red的值为默认值0  green为我们指定的5 blue则按照上一位的下标来决定此时应是6
// let blue:color3 = color3.blue
// console.log(blue)

// enum res {success=1,error=-1}  // 不使用下标,而使用我们自己定义的值
// let f:res = res.error
// console.log(f)
// console.log(res.success)


// // 定义任意类型any(对于一些无法确定的类型比如DOM比如Object我们使用any)
// let no_name:any = 123
// no_name = 'python'
// no_name = false
// no_name = {}
// // let box1 = document.querySelector('.box1')  // 虽然运行正常但是会提示警告
// // box1.style.color='red'

// let box1:any = document.querySelector('.box1')  // 
// box1.style.color='red'


// // 定义 null, undefined
// // let num:number  // 这里只定义但是并没有赋值虽然正常运行但是下面会警告
// // console.log(num)

// // let num:undefined  // 手动指定num的类型为undefined
// // console.log(num)

// let num:number | undefined  // 为变量指定两个可能存在的类型
// console.log(num)
// let a:undefined
// // a=null // 报错
// a = undefined

// // let n:null // null类型不允许赋值
// let n:null | number
// n = 123
// let b:null
// // b = undefined  //报错
// b = null


// // 定义void 无类型(通常用于定义某些方法或者函数无返回值的)
// let f1 = (age:number):void => {}
// f1(18)
// let f2 = function f2():undefined {}  // 错误写法
// let f2 = function f2():string {return 123}  // 返回值错误


// 定义never类型是其他类型(包括null和undefined)的子类型,代表从不会出现的值
// 这意味着,声明never的变量只能被never类型锁赋值
// let c:never
// c = (()=>{
//     throw new Error('错误')
// })()




// // es5函数的定义
// function f3(){}  // 传统函数
// let f4 = ()=>{}  // 匿名函数

// // ts函数的定义
// function f5(name:string|number[],age:number):string{return 'hello'}
// let f6 = function(name:string|Array<number>,age:number):number{return 23}
// let f7 = (name:string|any,age:number):void=> {}

// // ts中的可选参数(如果有一些参数可传可不传则使用 ? 来设置可选参数, 注意!: 可选参数必须配置到参数的最后面)
// let f8 = (name:string,age?:number)=>{if(age){console.log(`${name}今年${age}岁了`)} else{console.log(`${name}的年龄保密`)}}
// f8('小明')

// // ts中的默认参数(用来给一些参数设置默认值)
// let f9 = (name:string,age:number=18):void=>{console.log(`${name},今年${age}岁了`)}
// f9('小明',12)
// f9('小芳')

// // ts中的收集参数
// let f10 = (name:string,age:number,...args:Array<number>):void=>{console.log(args)}
// f10('小明',18,1,2,3,4,5)

// // ts中的函数重载(当有重名的函数的时候,对传入的参数约束会进行合并) 注意!: 不能使用匿名函数, (当两个及以上同名的函数但是他们的参数不一样,这时候会出现函数重载现象) 类似python中的"单分派泛函数",但是ts这里有点脱裤子放屁的感觉,如果不想调用的时候传递其他类型的参数完全可以 使用 string|number 限制传入的和传出的类型即可
// function f11(name:string):string;
// function f11(age:number):string;
// function f11(_flag:any):any {
//     if (typeof _flag == 'string'){
//         return `我叫${_flag}`
//     }
//     return `我今年${_flag}岁`
// }


// // ts中的类
// class Student{
//     name:string  // 实例属性,会挂载到原型上
//     constructor(name:string,age?:number){  // 构造函数,实例化类的时候触发的方法
//         this.name = name
//     }

//     run():void{  // 实例方法,会挂载到原型上
//         console.log(`调用了实例方法,我是${this.name}`)
//     }
//     getname():string{
//         return this.name
//     }
//     setname(name:string):void{
//         this.name = name
//     }
// }
// let stu1 = new Student('小明',18)
// stu1.run()
// stu1.setname('张三')
// console.log(stu1.getname())


// // ts中的继承
// class stu extends Student{
// }

// let stu2 = new stu('李四',22)
// console.log(stu2.getname())
// stu2.setname('李四啊')  // 修改了名字
// stu2.run()

// console.log('------ts中的继承与super------')
// // ts中的super, 子类如果重写构造函数,那么构造函数必须执行一次super
// // super可以当做函数使用也可以当做对象使用, 当做函数使用的时候代表了父类的构造函数,但是返回的是子类的实例
// // super当做对象使用的时候则指向了父类的原型对象
// class stu extends Student{
//     constructor(name:string){
//         // super(name)

//         console.log(super(name))  // 当做函数调用时super返回的和子类实例是一样的
//         console.log(this)

//         this.name = `子类-${name}`

//         console.log(super.getname == Student.prototype.getname)  // 当做对象的时候,super则代表了父类的原型对象
//     }
//     run(){
//         console.log(`调用了子类的方法: ${this.name}`)
//     }
// }

// let stu3 = new stu('李四')
// console.log(stu3.getname())
// stu3.run()


// ts中的类修饰符(属性不加修饰符默认为公有)
// public: 公有, 在类,子类,实例 中都可以访问该属性或者方法(这里就不做演示了)
// protected: 受保护的,在类,子类中可以访问,无法被实例访问
// private: 私有, 只能在类本身访问, 无法在子类和实例中访问
// class Student{
//     protected name:string
//     private age:number|undefined
//     constructor(name:string,age?:number){
//         this.name = name
//         this.age = age
//     }
//     run(){
//         console.log(this.name)
//         console.log(this.age)
//     }
// }

// class Stu extends Student{

//     run(){
//         console.log(this.name)  // 子类中在类中可以访问被保护的属性
//         // console.log(this.age)  // 私有属性访问直接报错了
//     }
// }

// let stu1 = new Student('小花',19)
// // console.log(stu1.name)  // 实例无法访问受保护的属性
// // console.log(stu1.age)  // 私有属性无法访问
// stu1.run()  // 无论是公有私有还有受保护在当前类的方法内部中都能访问

// let stu2 = new Stu('小明',22)
// stu2.run()


// // ts中的类方法静态方法和静态属性, 静态方法内无法访问实例属性,只能访问静态属性,静态方法只能类调用,实例无法调用
// class Student{
//     public name:string
//     static age:number = 19
//     constructor(name:string){
//         this.name = name
//     }

//     static run():void{
//         console.log(`this is static a function`)
//         // console.log(this.name)  // 无法访问,因为在创建类的时候还没有实例化的属性
//         console.log(Student.age)
//     }
//     say(){
//         console.log(this.name)
//     }
// }
// console.log(Student.age)
// Student.run()


// // ts多态(详见本博客Python版 http://152.69.196.199/index.php/archives/236.html )

// // ts抽象方法(详见本博客Python版 http://152.69.196.199/index.php/archives/234.html, 在ts中抽象类作为其他类的基类,不能被实例化,抽象类中的抽象方法不包含具体的实现,实现方法必须在派生类中,在ts中使用 abstract 抽象方法只能放在抽象类中)
// abstract class Animal{
//     public name:string
//     constructor(name:string){
//         this.name = name
//     }
//     abstract eat():any;
// }

// class Dog extends Animal{
//     eat(){
//         console.log(`${this.name}在吃东西`)
//     }
// }
// let dog = new Dog('小狗')
// dog.eat()


// ts中的接口(接口是一种规范的定义,定义了行为和动作规范,接口定义了某一批类所需要准守的规范,而不关系这些类或函数内部的状态数据,也不关心类中方法的细节)
// 为什么函数中有了类型约束还要搞接口? 概念不一样两者不会相互影响是独立的
// // 1. 自定义方法对json进行约束
// let f12 = function(jsonstr:{'lable':string,'age':number}):void{
//     console.log(jsonstr.lable,jsonstr.age)
// }
// f12({'lable':'张三',age:19})

// // 2. 使用接口进行约束
// interface FullName{
//     name:string;
//     age?:number
// }

// let f13 = (jsonstr:FullName)=>{
//     // 必须传入一个对象且有 name和age
//     console.log(jsonstr.name,jsonstr.age)
// }
// f13({'name':'李四','age':18})

// // f13({'name':'李四','age':18,'gender':'man'})  // 如果内部的元素比约束条件多,则需要使用对象的方式传入函数中
// let obj = {'name':'李四','age':18,'gender':'man'}
// f13(obj)

// 函数类型接口(对方法传入的参数以及返回值进行约束)
// interface encrypt{
//     (key:string,value:string):string
// }

// let md5:encrypt = (key:string,value:string):string=>{return `${key} + ${value}`}
// console.log(md5('hello','world'))

// 可索引接口
// 1. 数组的接口
// interface Arr{
//     [index:number]:string
// }
// let arr1:Arr = ['小明','小花']
// console.log(arr1)
// 对象的接口
// interface obj{
//     [index:string]:any
// }
// let stu:obj = {'namg':'张三','age':18}

// 类类型接口约束(对类进行的约束,和抽象类有点相似)
// interface Animal{
//     name:string  // 必须有name属性
//     eat(str:string):void  // 必须要有eat方法
// }

// class Dog implements Animal{
//     name:string
//     constructor(name:string){
//         this.name = name
//     }
//     eat(str:string){
//         console.log(`${this.name}在吃${str}`)
//     }
// }
// let dog = new Dog('小狗')
// dog.eat('骨头')

// 类类型接口的继承
// interface Animal{
//     eat():void
// }
// interface Person extends Animal{
//     say():void
// }


// class P1{
//     eat(){console.log('吃饭')}
// }
// class P2 extends P1 implements Person{
//     say(){console.log('说话')}
// }
// let p = new P2
// p.eat()
// p.say()


// ts中的泛型(一个组件可以支持任意数据类型,提高复用性,以及对不特定数据类型的支持)
// 1. 不使用泛型
// function f4(value:string):string{  // 这个函数只能传入和返回string类型的数据(如果需要返回不同类型则需要重新定义一个新的函数,造成代码冗余)
//     return ``
// }
// function f5(value:number):number{  // 这个函数只能传入和返回number类型的数据(如果需要返回不同类型则需要重新定义一个新的函数,造成代码冗余)
//     return 0
// }
// function f6(value:any):any{}  // 使用 any 类型这样虽然可以返回任意类型,但是却放弃了 类型检查

// 2. 大写的T表示泛型,具体是什么类型则是在调用这个方法的时候决定的
// function f7<T>(value:T):T{
//     return value
// }

// console.log(f7<string>('小明'))  // 在函数调用的时候指定传入的数据类型
// console.log(f7<number>(123))

// // 3. 定义泛型接口1(在函数调用时指定参数类型)
// interface say {
//     <T>(value:T):T
// }

// let say_hello:say = <T>(value:T):T => {return value}

// console.log(say_hello<string>('123'))
// console.log(say_hello<number>(2233))
// // console.log(say_hello<number>('abcd'))  // 错误写法


// // 3.1 定义泛型接口2(创建函数时指定该函数的泛型接口,让函数只能使用指定类型的参数)
// interface say <T>{  // 泛型接口
//     (value:T):T
// }
// // let say_hello:say<string> = <T>(value:T):T=> value;  // 定义一个指定泛型接口的函数
// let say_hello:say<string> = (value)=> value;  // 定义一个指定泛型接口的函数
// say_hello('abc')
// // say_hello(123)  // 错误写法

// 3.2 泛型类(在实例化的时候指定类中数据使用的类型)
// 普通的类写法
// class math{
//     public li:string[] = []
//     append(value:string):void{
//         this.li.push(value)
//     }
//     min():string{
//         let flag:string = this.li[0]
//         for(let i of this.li){
//             if(flag > i) flag = i
//         }
//         return flag
//     }
// }
// let m = new math
// m.append('b')
// m.append('c')
// m.append('a')
// console.log(m.min())

// 泛型类写法(可以在实例化的时候指定所要使用的类型)
// class math2<T>{
//     public li:T[] = []
//     append(value:T):void{
//         this.li.push(value)
//     }
//     min():T{
//         let flag:T = this.li[0]
//         for(let i of this.li){
//             if(flag > i) flag = i
//         }
//         return flag
//     }
// }
// let m2 = new math2<number>()
// m2.append('b')  // 错误的写法
// m2.append('c')
// m2.append('a')
// console.log(m2.min())


// // 自定义普通类
// class User{  // 定义一个用户类
//     username:string
//     password:string
//     age?:number
//     constructor(username:string,password:string,age?:number){
//         this.username = username
//         this.password = password
//         this.age = age
//     }
// }
// class Article{  // 定义一个文章类
//     title:string
//     info:string
//     constructor(title:string,info:string,){
//         this.title = title
//         this.info = info
//     }
// }
// // 增加管理用户类的mysql工具
// class MysqlDB{
//     add(user:User):boolean{
//         console.log(user)
//         return true
//     }
// }

// let user1 = new User('张三','asd123')
// let mysql = new MysqlDB()  // 这个实例化的类只能对User类使用add,因为add的类型检查就是User类,如果再增加一个  文章类, 那么这个实例化的mysql类就没用了
// mysql.add(user1)

// // 增加管理文章类的 mysql工具
// let article1 = new Article('水浒传','四大名著之一')
// // mysql.add(article1)  // 错误写法,如果想增加管理article类的功能需要重新写MysqlDB的类
// class MysqlDB_Art{
//     add(article:Article):boolean{
//         console.log(article)
//         return true
//     }
// }
// let mysql_art = new MysqlDB_Art
// mysql_art.add(article1)


// 自定义泛型类(可以在类实例化的时候指定实例化后的类可以操作的数据类型)
// class Article{  // 定义一个文章类
//     title:string
//     info:string
//     constructor(title:string,info:string,){
//         this.title = title
//         this.info = info
//     }
// }
// class User{  // 定义一个用户类
//     username:string
//     password:string
//     age?:number
//     constructor(username:string,password:string,age?:number){
//         this.username = username
//         this.password = password
//         this.age = age
//     }
// }
// class Mysql_utils<T>{  // 自定义数据库泛型类在实例化的时候可以指定改实例化的sql类操作的数据类型
//     add(flag:T):boolean{
//         console.log(flag)
//         return true
//     }
//     update(flag:T,title:string):void{
//         console.log(title, flag)
//     }
// }
// let user1 = new User('张三','asd123')
// let art1 = new Article('水浒传','四大名著之一')
// let Sql_user = new Mysql_utils<User>()
// let Sql_art = new Mysql_utils<Article>()
// Sql_art.add(art1)
// Sql_user.add(user1)



// 定义一个BooksCreate类模拟ORM
// class BooksCreate{
//     title:string
//     info:string
//     constructor(data:{title:string,info:string}){  // 对形参设置接口约束
//         this.title = data.title
//         this.info = data.info
//     }
// }

// let book_db = new Mysql_utils<BooksCreate>()
// let book1 = new BooksCreate({'title':'水浒传','info':'四大名著之一'})
// book_db.add(book1)
// book_db.update(book1,'西游记')





// 自定义一个数据库类来分别对不同的数据库进行操作
// interface dbface<T>{  // 定义接口约束
//     add(info:T):boolean
//     update(info:T,id:number):boolean
//     get(id:number):any[]
//     delete(id:number):boolean
// }

// // 定义一个操作mysql数据库的类 
// class MysqlDB<T> implements dbface<T>{
//     add(info: T): boolean {
//         console.log(`增加成功`,info)
//         return true
//     }
//     update(info: T, id: number): boolean {
//         throw new Error("Method not implemented.");
//     }
//     get(id: number): T[] {
//         // 根据id找到用户,返回带用户逇数组
//         let li:T[] = []
//         return li
//     }
//     delete(id: number): boolean {
//         throw new Error("Method not implemented.");
//     }
// }

// class MongoDB<T> implements dbface<T>{
//     add(info: T): boolean {
//         throw new Error("Method not implemented.");
//     }
//     update(info: T, id: number): boolean {
//         throw new Error("Method not implemented.");
//     }
//     get(id: number): any[] {
//         throw new Error("Method not implemented.");
//     }
//     delete(id: number): boolean {
//         throw new Error("Method not implemented.");
//     }
// }

// class User{  // 自定义一个用户类
//     name:string
//     password:string
//     constructor(data:{name:string,password:string}){
//         this.name = data.name
//         this.password = data.password
//     }
// }

// let u1 = new User({name:'张三',password:'asd'})
// let mysql = new MysqlDB<User>()

// mysql.add(u1)
// mysql.get(123)



// ts中的导入模块(需要在终端中使用node命令运行编译后的js文件,真tm的拉垮,不能使用 : 起别名,需要使用 as 关键字起别名)
// import {get_data as get,url} from './db'
// console.log(get)
// console.log(url)


// // ts中的命名空间(注意命名空间里面的变量需要暴露出来才能使用)
// namespace A {
//     export let flag:string = '张三'
// }
// namespace B {
//     export let flag:string = '李四'
// }
// console.log(B.flag)


// ts中的装饰器(类装饰器, 属性装饰器, 方法装饰器, 参数装饰器)

// // 普通装饰器(如果装饰器函数中没有返回则默认返回传进来的参数)
// let log_utils = (parms:any):any=>{
//     parms.prototype.url = '152.69.196.199'  // 给传进来的对象的原型对象上增加属性
//     console.log(parms)
//     return 123
// }

// @log_utils
// class HttpClint{
//     constructor(){

//     }

//     get_data():void{
//         console.log('获取数据')
//     }
// }
// console.log(HttpClint)

// 参数装饰器(装饰类时:内部函数接收一个参数,类的构造函数)
// let log_utils = (parms:any):any=>{
//     let warpper = function(target:any){  // 类的构造函数
//         console.log(parms)
//         console.log(target)
//         target.prototype.url = parms
//     }
//     return warpper
// }
// @log_utils('www.baidu.com')
// class HttpClint{
//     constructor(){
//     }
//     get_data():void{
//         console.log('获取数据')
//     }
// }
// let http = new HttpClint
// console.log(http.constructor)

// // 类装饰器修改当前类的构造函数(类装饰器表达式会在运行时当做函数调用,类的构造函数作为唯一参数)
// // 装饰器内对类重载
// let log_utils = function(target:any):any{

//     return class extends target {
//         url = '修改后的数据'
//     }
// }
// @log_utils
// class HttpClint2{
//     public url?:string
//     constructor(){
//         this.url = '我是构造函数中的url'
//     }

//     get_data():void{
//         console.log(this.url)
//     }
// }
// let http2 = new HttpClint2()
// http2.get_data()


// 装饰器传参,类属性装饰器()
// let log_property = function(parmas:any,){
//     return function(target:any,attribute:any){  // 修改属性时:内部函数接收两个参数,对于静态成员则是构造函数对于实例成员则是原型对象, 属性名称
//         console.log(parmas)
//         console.log(target)
//         console.log(attribute)
//         target[attribute] = parmas
//     }
// }
// class HttpClint3{
//     @log_property('www.baidu.com')
//     public url?:string
//     @log_property('www.qq.com')
//     static url2?:string
//     constructor(){
//     }

//     get_data():void{
//         console.log(this.url)
//     }
// }
// let http = new HttpClint3
// http.get_data()

// 类方法装饰器(会被应用到方法的属性描述符上,用来监听修改或者替换方法的定义,方法装饰器传入三个参数, 对于静态成员则是构造函数对于实例成员则是原型对象,成员的名字,成员的属性描述符)
// let log_method = function(parmas:any){
//     return function(target:any,method_name:any,desc:any){
//         console.log(target) 
//         console.log(method_name)
//         console.log(desc)
//         // 修改方法
//         let _method:any = desc.value
//         desc.value = function(...args:any){
//             for(let i of args){
//                 console.log(i)
//             }
//             console.log('运行原方法之前自定义一些代码运行')
//             _method.apply(this,args)
//         }
//         // 增加方法
//         target.say = function(){
//             console.log('hello')
//         }
//     }
// }
// class HttpClint3{
//     constructor(){
//     }
//     @log_method('123')
//     public say_hi(...args:any):void{
//         console.log('hi,这是原生方法传进来的参数有: ',args)
//     }
//     @log_method('123')
//     static say_hello():void{
//     }
// }
// let http:any = new HttpClint3
// http.say()
// HttpClint3.say()
// // 调用被修改后的方法
// http.say_hi(1,2,3,4,5)


// 方法的参数装饰器(传入三个参数,1.对于静态成员是构造函数,对于实例成员是原型对象, 2.方法的名字, 3.参数在方法的所有的参数列表中的索引)
let logProams = function (params:any){
    return function(target:any,method_name:any,idx:any){
        console.log(params)
        console.log(target)
        console.log(method_name)
        console.log(idx)

    }
}

class Animal{
    say(@logProams('hi') str:any){
        console.log(str)
    }
}
let dog = new Animal
dog.say('hello')



// 装饰器的执行顺序
// 属性装饰器->方法装饰器->方法参数装饰器->类装饰器
// 如果有多个装饰器则从右到左,从下到上依次执行(上电梯大法)