ts学习笔记

配置

  • tsc --init 生成tsconfig.json
  • vscode 任务-运行任务 tsc:监视-tsconfig.json

数据类型

  • boolean, number, string, array, null, undefied, void

  • 元组类型tuple

    let tuple:[number, string] = [1, 's']
    
  • 枚举类型enum

    enum Color {
        red,
        blue,
        yellow = 5,
    }
    let color:Color = Color.yellow // 5
    color = Color.yellow // 0, 未赋值则输出下标
    
  • 任意类型any

  • null和undefined是其他(never)类型的子类型

  • void类型: ts中void类型表示没有任何类型,一般用于定义方法时,方法没有返回值

  • never类型:其他类型(null和undefined)的子类型

函数

// 函数声明
function run():string {
    return '123'
}

// 匿名表达式
var run2 = function():string {
    return '123'
}

// ts中定义方法传参
function getInfo(name: string, age: number):string {
    return `${name}:${age}`
}

// 方法可选参数(age可传,可不传)
// 可选参数要放在所有参数的最后
function getInfo(name: string, age?: number):string {
    if (age) {
        return `${name}:${age}`
    }
    return `${name}`
}

// 默认参数
function getInfo(name: string, age: number = 20):string {
    if (age) {
        return `${name}:${age}`
    }
    return `${name}`
}

// 剩余参数
function sum(a: number, b:number, c:number, d:number):number {
    return a + b + c + d;
}
可改为
function sum(...res: number[]):number {
  return res.reduce((acc, cur) => acc + cur, 0)
}

// ts函数重载
function getInfo(name: string): string;
function getInfo(age: number): number;

function getInfo(arg: any): any {
  return 123;
}
// 箭头函数

ts中的类

  • 类的定义、继承
// es5
function Person(name) {
  this.name = name;
  this.run = function () {
    console.log(this.name)
  }
}
var p = new Person('张三')
p.run()


// ts中定义类
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.setName('李四')
alert(p.getName())

// ts中实现继承 extends, super

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

class Web extends Person {
  constructor(name: string) {
    super(name)
  }
}

var w = new Web('lisi')
console.log(w.run())

// ts中继承的探讨
class Person {
  name: string; // 属性,省略了public关键词
  constructor(name:string) { // 构造函数,实例化类时触发的方法
    this.name = name;
  }
  run():string {
    return `${this.name} is running`
  }
}

class Web extends Person {
  constructor(name: string) {
    super(name)
  }
  run():string {
    return '子类' //父类中也有同名方法,则调用子类的方法
  }
  work() {
    alert(`${this.name}在工作`) // this指向web实例
  }
}

var w = new Web('lisi')
console.log(w.run())

类里的修饰符

  • ts里定义属性的时候,给我们提供了三种修饰符
/* 
    public:公有 在类里面,子类和类外面都可以访问
    protected:保护类型 在类里面、子类里面可以访问,在类外部没法访问
    private:私有 在类里面可以访问,子类和类外部都没法访问
*/

// public
// 属性不加任何修饰符为public

// 类外部访问公有属性
class Person {
  public name: string;
  constructor(name:string) {
    this.name = name;
  }
}
var p = new Person('lala');
console.log(p.name)

// protected

// 在子类中访问
class Person {
  protected name: string; // 属性,省略了public关键词
  constructor(name:string) { // 构造函数,实例化类时触发的方法
    this.name = name;
  }
}
class Web extends Person {
  constructor(name: string) {
    super(name)
  }
  run():string {
    return `${this.name}lalalla`
  }
}
var w = new Web('ab');
console.log(w.run())

// private 只能在类内部访问
class Person {
  private name: string; // 属性,省略了public关键词
  constructor(name:string) { // 构造函数,实例化类时触发的方法
    this.name = name;
  }
}


静态属性 静态方法

  • es5
// 实例方法
function Person() {
  this.run1 = function() {
    ...
  }
}

// 静态方法
Person.run2 = function() {

}

// 静态属性
Person.name = 'lalal'


var p = new Person();
p.run1()

Person.run2()
  • 已经有实例方法,为啥要有静态方法

    // jquery
    
    function $(element) {
        return new Base(element)
    }
    
    function Base (element) {
        this.element = document.getElementById(element);
        this.css = function (attr, val) {
            this.element.style.attr = val
        }
    }
    // 实例方法
    $('#box').css('color', 'red')
    // 静态方法
    $.get('url', function() {...})
    
  • ts里静态方法和属性

class Person {
  public name: string
  public age: number = 20
  // 静态属性
  static sex: string = '男'
  constructor(name:string) {
    this.name = name;
  }
  // 实例方法
  run() {
    alert(`${this.name}is running`)
  }
  work() {
    alert(`${this.name}is working`)
  }
  // 静态方法 里面没法直接调用类里面的属性!!!
  static print() {
    alert(`print+${Person.sex}`)
  }
}

Person.print()

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

  • 多态属于继承
class Animal {
  name: string
  constructor(name: string) {
    this.name = name
  }
  eat() { // 具体的内容,让继承他的子类去实现,每个子类有不同表现
    console.log('eat')
  }
}

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 + '吃鱼'
  }
}

var cat = new Cat('cattie')
alert(cat.eat())

ts中的抽象类,提供其他类继承的基类,不能直接被实例化

  • 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并必须在派生类中实现
  • abstract抽象方法只能放在抽象类里面
  • 抽象类和抽象方法用来定义标准
// Animal类的子类必须包含eat方法
abstract class Animal {
  public name: string;
  constructor(name:string) {
    this.name = name
  }
  abstract eat():any;
}

// 抽象类的子类必须实现抽象类里的抽象方法
class Dog extends Animal {
  constructor(name:string) {
    super(name)
  }
  eat() {
    return this.name + 'something'
  }
}

var d = new Dog('abctest+')
alert(d.eat())

ts中的接口

  • 定义标准(包括属性,函数,可索引, 类等)

属性接口 对json的约束

// 接口 行为和动作的规范 对批量方法传入参数进行约束
// 对传入对象的约束, 属性接口
interface FullName {
  firstName: string; // 注意!!分号结束
  secondName: string;
}

function printName(name: FullName) {
  // 必须传入对象,必须包含fistName,secondName
  console.log(`${name.firstName} ${name.secondName}`)
  // console.log(`${name.firstName} ${name.secondName} ${name.age}`) // name.age 报错
}

// 直接传入参数,则有且仅有firstName和secondName,否则报错
// printName({ firstName: 'zhang', secondName: 'san' age: 20 }) // age报错

// 定义在外面 再传入,则必须包含firstName和secondName,多的参数也行, 但在方法里调用fullName不存在的属性也会报错
const obj = {
  firstName: 'zhang',
  secondName: 'san',
  age: 20
}

printName(obj)
  • 接口 可选属性
interface FullName {
  firstName: string;
  secondName?: string;
}

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

interface encrypt {
  (key: string, val: string):string
}

var md5:encrypt = function(key: string, val: string):string {
  return `${key} + ${val}`
}

console.log(md5('test', '123'))

可索引接口,对数组/对象的约束

// 可索引接口 对数组的约束
interface UserArr {
  [index: number]: string
}

var arrTest: UserArr = ['123', '112']

console.log(arrTest[0])

// 可索引接口 对对象的约束
interface UserObj {
  [index: string]: string
}

var usrObj:UserObj = {
  name: '20'
}

console.log(usrObj.name)

类类型接口

// 类类型接口:对类的约束 和抽象类有点类似
interface Animal {
  name: string;
  eat(str: String):void;
}

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

var d = new Dog('xxiaohei')
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('miaomiao')
c.eat('laoshu')

接口的扩展、接口的继承(接口可以继承接口)

// 例子一
interface Animal {
  eat():void
}

interface Person extends Animal {
  work():void
}

class Web implements Person {
  public name: string;
  constructor(name:string) {
    this.name = name
  }
  eat() {
    console.log(`${this.name} --- chi`)
  }
  work() {
    console.log(`${this.name} --- work`)
  }
}

var ww = new Web('webbbb')
ww.eat()
ww.work()

// 例子二 继承 + 实现(接口扩展)
interface Animal {
  eat():void
}

interface Person extends Animal {
  work():void
}

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

}

class Web extends Programmer implements Person {
  constructor(name:string) {
    super(name)
  }
  eat() {
    console.log(`${this.name} --- chi`)
  }
  work() {
    console.log(`${this.name} --- work`)
  }
}

var ww = new Web('webbbb')
ww.eat()
ww.work()
ww.coding('ts')

泛型

  • 解决类 ,接口,方法的复用性, 以及对不特定数据类型的支持
  • 泛型可以支持不特定的类型数据,要求:传入的参数和返回的参数一致

泛型的定义,泛型函数

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

// getData('test');  错误写法
getData(123); // 参数必须是number

泛型类

// 最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现
class MinClass {
  public list: T[] = []
  add(num:T) {
    this.list.push(num)
  }
  min():T {
    var minNum = this.list[0]
    for (let i = 0; i < this.list.length; i++) {
      if (this.list[i] < minNum) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}

var m = new MinClass() // 实例化类,且制定T代表number

m.add(2)
m.add(3)
m.add(4)

alert(m.min())

var m2 = new MinClass()
m2.add('44')
m2.add('a')
m2.add('v')
alert(m2.min())

泛型接口

// 1. 第一种定义泛型接口的方法
interface ConfigFn {
  (val:T):T
}

var getData:ConfigFn = function(value: T) {
  return value
}

getData('123')

// 2.第二种定义泛型接口的方法
interface ConfigFn {
  (val:T):T;
}

function getData(value: T):T {
  return value
}

var myGetData:ConfigFn = getData;

myGetData('20')

把类作为参数类型的泛型类

  1. 先定义个类
  2. 把类作为参数来约束数据传入的类型

功能一

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 = 'zhangsan'
u.password = '123456'

var db = new MysqlDb()
db.add(u)

功能二

class ArticleCate {
  title: string | undefined;
  desc: string | undefined;
  status: number | undefined;
}
class MysqlDb {
  add(info: ArticleCate): boolean {
    console.log(info)
    return true
  }
}

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

var DB = new MysqlDb()
DB.add(a)

但mySqlDb重复封装了,通过泛型解决

// 操作数据库的泛型类
class MysqlDb  {
  add(info: T): boolean {
    console.log(info)
    return true
  }
  update(info: T, id: number):boolean {
    console.log(info)
    console.log(id)
    return true
  }
}

// 1.定义一个user类和数据库进行映射
class User {
  username: string | undefined;
  password: string | undefined;
}

var u = new User()
u.username = 'zhangsan'
u.password = '123456'

var DB = new MysqlDb()

DB.add(u)

// 1.定义一个ArticleCate类和数据库进行映射
class ArticleCate {
  title: string | undefined;
  desc: string | undefined;
  status: number | undefined;
  constructor(params: {
    title?: string,
    desc?: string,
    status?: number ,
  }) {
    this.title = params.title
    this.desc = params.desc
    this.status = params.status
  }
}

var a = new ArticleCate({
  title: '分类',
  desc: '111'
})

var DB2 = new MysqlDb()

DB2.add(a)
DB2.update(a, 2)

ts类型,接口,类,泛型综合使用

interface DBI {
  add(info: T):boolean;
  update(info: T, id: number):boolean;
  delete():boolean;
  get(id: number): any[]
}

// 定义一个操作mySql数据库的类 注意:要实现泛型接口,这个类也应该是一个泛型类
class MysqlDb implements DBI {
  add(info: T): boolean {
    console.log(info)
    return true
  }
  update(info: T, id: number): boolean {
    throw new Error("Method not implemented.");
  }
  delete(): boolean {
    throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
    var list = [
      {
        title: 'xxxx',
        desc: 'xxx1'
      },
      {
        title: 'xxxx',
        desc: 'xxx1'
      },
    ]
    return list
  }
}

// 定义一个操作mySql数据库的类 注意:要实现泛型接口,这个类也应该是一个泛型类
class MssqlDb implements DBI {
  add(info: T): boolean {
    console.log(info)
    return true
  }
  update(info: T, id: number): boolean {
    throw new Error("Method not implemented.");
  }
  delete(): boolean {
    throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
    var list = [
      {
        title: 'xxxx',
        desc: 'xxx1'
      },
      {
        title: 'xxxx',
        desc: 'xxx1'
      },
    ]
    return list
  }
}

// 操作用户表 定义一个user类和数据表的映射
class User {
  username: string | undefined;
  password: string | undefined;
}

var u = new User()
u.username = 'zhangsan'
u.password = '123456'

var oMySql = new MysqlDb(); // 用类约束传入参数的合法性
oMySql.add(u)
console.log(oMySql.get(4))


var oMsSql = new MssqlDb()
oMsSql.add(u)
console.log(oMsSql.get(1))

ts 模块化封装

模块的定义,引用

// db.ts
var dbUrl = 'xxx'

function getData():any[] {
  console.log('获取数据库数据')
  var list = [
    {
      title: '222'
    }, {
      title: '333'
    }
  ]
  return list
}

function saveData() {
  console.log('保存')
}

export {
  dbUrl,
  getData,
  saveData
}

// export default getData // export default在一个模块中只能用一次,引入时import getData from ’xxxx/xxx‘

// index.ts

import { getData as get, saveData } from './modules/db'

get()
saveData()

应用举例:封装db库

// modules/db.ts

interface DBI {
 add(info: T):boolean;
 update(info: T, id: number):boolean;
 delete():boolean;
 get(id: number): any[]
}

export class MssqlDb implements DBI {
 add(info: T): boolean {
   console.log(info)
   return true
 }
 update(info: T, id: number): boolean {
   throw new Error("Method not implemented.");
 }
 delete(): boolean {
   throw new Error("Method not implemented.");
 }
 get(id: number): any[] {
   var list = [
     {
       title: 'xxxx',
       desc: 'xxx1'
     },
     {
       title: 'xxxx',
       desc: 'xxx1'
     },
   ]
   return list
 }
}

// model/user.ts

import { MssqlDb } from '../modules/db'

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

var UserModel = new MssqlDb(); // 用类约束传入参数的合法性

export {
 UserClass,
 UserModel
}

// index.ts

import { UserClass, UserModel } from './model/user'
import  {ArticleClass, ArticleModel } from './model/article'

var user = new UserClass();
user.username = 'zhangsan'
user.password = '123344'

UserModel.add(user)
var res = UserModel.get(123)
console.log(res)

命名空间 命名空间块化

namespace A {
  // ...
}

namespace B {
  // ...
  export class Dog {
    name: string;
    constructor(name:string) {
      this.name = name
    }
  }
}

// 调用命名空间中的方法,或类,需要在namespace中暴露export出来
var d = new B.Dog('jfsldjf')
console.log(d)

可将命名空间当作一个模块直接暴露出来

// modules/a.ts
export namespace A {
  // ...
}

// index.ts
import { A } from 'xxxx'
var d = new A.dog('test')

命名空间和模块的区别:命名空间主要用于组织代码,避免命名冲突;模块侧重代码的重用,一个模块可能会有多个命名空间

装饰器

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

常见装饰器有:类装饰器,属性装饰器,方法装饰器,参数装饰器

装饰器的写法:普通装饰器(无法传参);装饰器工厂(可传参)

类装饰器

普通装饰器(无法传参)

// 类装饰器,在类声明之前被声明(紧接着类声明),应用于类构造函数,可用来监视、修改、或替换类定义

// 装饰器
function logClass(params:any) {
  console.log(params) // params 就是当前类
  params.prototype.apiUrl = 'xxxx'
  params.prototype.run = function() {
    console.log('run method')
  }
}

@logClass
class HttpClient {
  constructor() {

  }
  getData() {

  }
}

var http: any = new HttpClient()
console.log(http.apiUrl)
http.run()

装饰器工厂(可传参数)

// 装饰器工厂
function logClass(params: string) {
  return function(target: any) { // target为类
    // console.log(target, params)
    target.prototype.apiUrl = params
  }
}

@logClass('http://hello')
class HttpClient {
  constructor() {

  }
  getData() {

  }
}

var http:any = new HttpClient()
console.log(http.apiUrl)

类装饰器:下面是一个重载构造函数的例子, (修改当前类的构造函数或方法,可看为固定用法)

function logClass(target:any) {
  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()

属性装饰器

// 属性装饰器
// 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
// 1. 对静态成员来说是类的构造函数,对实例成员来说是类的原型对象
// 2. 成员的名字
function logProperty(params:any) {
  return function (target:any, attr: any) {
    console.log(target, attr)
    target[attr] = params
  }
}

@logClass('http://hello')
class HttpClient {
  @logProperty('newUrl')
  public url: any | undefined
  constructor() {

  }
  getData() {
    console.log('getData===>', this.url)
  }
}

var http = new HttpClient()
http.getData()

方法装饰器

方法装饰器 会应用到方法的属性描述符上,可以用来监视,修改,或者替换方法定义

方法装饰器会在运行时,传入下列三个参数

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2. 成员的名字
3. 成员的属性描述符

方法装饰器一: 修改当前实例的属性和方法

function get(params:any) {
  return function(target:any, methodName: any, desc:any) {
    console.log(target, methodName, desc) // 仍可改变类原型上的方法和属性
    target.apiUrl = 'apiUro'
    target.run = function() {
      console.log('run')
    }
  }
}

class HttpClient {
  public url: any | undefined
  constructor() {

  }
  @get('htttp://dksjflsjkf')
  getData() {
    console.log('getData===>', this.url)
  }
}

var http:any = new HttpClient()
console.log(http.apiUrl)
http.run()

方法装饰器二: 修改当前方法

function get(params:any) {
  return function(target:any, methodName: any, desc:any) {
    console.log(target, methodName, desc.value)

    // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
    // 1. 保存当前方法
    var oMethod = desc.value;

    desc.value = function(...args: any[]) {
      args = args.map(val => String(val))
      console.log(args)
      // 2. 调用原来的旧方法(看是否需要直接覆盖,可省略)
      oMethod.apply(this, args)
    }
    
  }
}

class HttpClient {
  public url: any | undefined
  constructor() {

  }
  @get('htttp://dksjflsjkf')
  getData(...args:any[]) {
    console.log('我是getData', args)
  }
}

var http:any = new HttpClient()
http.getData(123, 'xdd')

方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,可用使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数

1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2. 方法的名字
3. 参数在函数参数列表中的索引
function logParam(params: any) {
  return function(target:any, methodName: any, paramsIndex:any) {
    // console.log(params)
    // console.log(target, methodName, paramsIndex)
    target.apiUrl = params
  }
}

class HttpClient {
  public url: any | undefined
  constructor() {

  }
  getData(@logParam('uuid') uuid:any) {
    // console.log(uuid)
  }
}

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

装饰器的执行顺序

  1. 属性 > 方法 > 方法参数 > 类装饰器
  2. 有多个同样的装饰器,则会先执行后面的
function logClass1(params:string) {
  return function(target:any) {
    console.log('类装饰器1')
  }
}

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

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

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

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

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

@logClass1('http://1')
@logClass2('http://2')
class HttpClient {
  @logAttr()
  public url: any | undefined
  constructor() {

  }
  @logMethod()
  getData() {
    return true
  }

  setData(@logParam1() attr1:any, @logParam2() attr2: any) {
  }
}

var http:any = new HttpClient()


// 打印结果是
/ *
    属性装饰器
    方法装饰器
    方法参数装饰器2
    方法参数装饰器1
    类装饰器2
    类装饰器1
*/

你可能感兴趣的:(ts学习笔记)