TypeScript总结

TypeScript笔记。一些特性总结

新的数据类型

元祖(Tuple)

属于数组的一种,数组合并了相同类型,元祖合并了不同类型

let tom: [string, number] = ['tom', 25]

枚举类型(enum)

将一些事物的状态和数值对应起来,尽量用自然语言中的含义清楚的单词来表示它的每一个值。

enum 枚举名 {
    标识符[=整形常数]
  标识符[=整形常数]
  标识符[=整形常数]
}

enum Flag {
  success = 1,
  error = 2
}

let s:Flag = Flag.success
console.log(s) // 1

enum Flag {
  success ,
  error 
} // 如果标识符没有对映数值,这位下标值

enum Color { // 用具体单词表示表示数字状态的字符串
    blue,
    red = 3,
    'orange'
}
console.log(Color['red'])//3
console.log(Color.orange) //4
consoloe.log(Color[0]) //blue 枚举值到枚举名也会进行反向映射

应用,比如说错误的HTTP状态码

任意类型(any)

void类型

void表示没有任何类型,一般将用于定义方式的时候没有返回值

never类型

基本上用不到

函数重载

  • java中方法的重载,指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。

  • typescript中的重载,通过为同一个函数提供多个函数类型定义来试下多种功能的母的。

    ES5不支持后写的同名函数会覆盖,TS支持

    function reverse(x: number): number;
    function reverse(x: string): string;
    function reverse(x: number | string): number | string {
        if (typeof x === 'number') {
            return Number(x.toString().split('').reverse().join(''));
        } else if (typeof x === 'string') {
            return x.split('').reverse().join('');
        }
    }
    

    重复定义了多次函数,前面两次都是定义,string返回string,number返回number;

    最后一次是函数实现。

typescript类

实例修饰符

TS可以使用三种修饰符(modifiers)public,private,protected,修饰实例属性可用范围。

  • public修饰的属性或方法是公有的,可以在任何地方被访问到,默认都是public,在类里面,之类,类外部都可以访问
  • protected修饰的属性或方法是受保护的,在类里面、子类里面可以访问,在类外部没法访问
  • private修饰的属性或方法是私有的,只能在当前类使用。

静态属性 静态方法

staticES6 ,静态只能调用静态

多态

  • 父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现。
  • 多态属于继承
class Animal {
  name: string;
  constructor(name:string){
    this.name = name
  }
  eat() {
    console.log('吃的东西')
  }
}
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 + '吃老鼠'
  }
}

抽象类

  1. typescript中的抽象类,它是提供其他类继承的基类,不能被实例化。

  2. abstract关键字定义抽象和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现

  3. abstract抽象方法只能放在抽象类里面

  4. 抽象类和抽象方法用来定义标准

  5. 抽象类的子类必须实现抽象类里面的抽象方法

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name
  }
  abstract sayHi() 
}

class Cat extends Animal {
  public sayHi() {
    console.log(`Tom, My name is ${this.name}`)
  }
}

let car = new Cat('Tom')

interface接口

在面向对象的编程语言中,接口是一种规范的定义,它定义里行为和动作规范,在程序设计里面,接口起到一种限制和规范的作用,接口定义了某一批类所需要遵守的规范。它是对行为的抽象,而具体如何行动需要由类去实现。

TypeScript中的接口可以用于

  • 对类的一部分行为进行抽象
  • 对对象的形状进行描述

属性类接口

interface Fullname {
  firstName:string;
  secondName:string;
}
function printName(name:Fullname){
  console.log(name.firstname + '-' + name.secondName)
}
//1. 外部包含接口就可以
const obj = {
  age:20,
  firstName: 'Tommy',
  secondName: 'Shen',
}
printName(obj)
//2.严格遵守接口
printName({
  firstName: 'Tommy',
  secondeName: 'Shen'
})
//3.接口可一批量对映多个函数

可选属性

interface Fullname {
  firstName:string;
  secondName?:string;
}

例子

interface Config {
    method:string;
    url:string;
    data?:string;
    headers?: any;
    dataType:string;
}

function ajax<T>(config:Config):Promise<T> {
    return new Promise((resolve, reject) => {
        let request = new XMLHttpRequest()
        request.open(config.method, config.url, true)
        for(let key in config.headers){
            let value = Headers[key]
            request.setRequestHeader(key, value)
        }
        request.onreadystatechange = () =>  {
            if(request.readyState === 4){
                if(request.status >= 200 && request.status < 300){
          if(config.dataType === 'JSON') {
            resolvs.call(undefined, JSON.parse(request.responseText))
          }else{
            resolve.call(undefined, request.responseText)
          }
                }else if(request.status >= 400) {
                    reject.call(undefined, request.responseText)
                }
            }
        }
    })
}

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

//对数组的约束
interface UserArr {
  [index:number]:string
}
//对象的约束
interface userObj {
    [index:string]:string
}
//类类型的接口 和 抽象类有点相似
interface Animal {
  name:string;
  eat(str:string):void
}

class Dog implement Animal {
  name:string;
  constructor(name:string){
    this.name = name
  }
  eat(){
    console.log(this.name + '吃肉肉')
  }
}

const d = new Dog('小黑')

接口扩展:接口可以继承接口

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 + '吃哒面王')
  }
  work(){
    console.log(this.name + '写代码')
  }
}

类与接口(为什么有了抽象类还需要接口)

implement是面向对象中的一个重要概念,一个类只能继承自另一个类;但不同类之间有一些共有的特性,这时候就可以把特性提取成接口,一个类可以实现多个接口,大大提高了面向对象的灵活性。

接口继承类 (重要 TS特有!!!)

常见的面向对象语言中,接口是不能继承类的,但是在TS中却是可以的

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface Pointed3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

为什么TypeScript会支持接口继承类呢?

实际上,在生命class Point时,除了会创建一个名为Point的类之外,同时也创建了一个名为Point的类型

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface PointInstanceType {
    x: number;
    y: number;
}

// 等价于 interface Point3d extends PointInstanceType
interface Point3d extends Point {
    z: number;
}

所以「接口继承类」和「接口继承接口」没有本质的区别

值得注意的是,PointInstanceType 相比于 Point,缺少了 constructor 方法,这是因为声明 Point 类时创建的 Point 类型是不包含构造函数的。另外,除了构造函数是不包含的,静态属性或静态方法也是不包含的(实例的类型当然不应该包括构造函数、静态属性或静态方法)。

总结: 声明Point类时创建的Point类型只包含其中的实例属性和实例方法

同样的,在接口继承类的时候,也只会继承它的实例属性和实例方法。

泛型

泛型: 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型

通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持

//泛型:可以支持不特定的数据类型
//要求:传入的参数和返回的参数一致
//T表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value:T):T {
    return value
}

//都是any 失去了类型检查的意义
function createArray(length: number, value: any): Array<any> {
    let result = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

//使用泛型,调用的时候传入数据类型
function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']

多个类型参数

//定义泛型的时候,可以一次定义多个类型参数
function swap<T, U>(tuple: [T, U]):[U, T]{
  return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]

交换输入的元祖

泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意操作它的属性和方法;这时可以对泛型进行约束。

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

使用extends约束了泛型T必须符合接口Lengthwise,也就是必须包含length属性。

泛型接口

//1.
interface ConfigFn{
  <T>(value:T):T;
}
//2.
interface ConfigFn<T>{
  (value:T):T;
}

const getData:ConfigFn = function<T>(value:T):T{
  return value
}
getData<string>('hahaha')

泛型类

与泛型接口类似,泛型也可以用于类的类型定义中

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型参数的默认类型

//当使用泛型时没有在代码中直接指定类型参数
//TS也无法推倒出来类型时,这个默认值就会起作用
function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

命名空间

命名空间和模块的区别:

  1. 命名空间:内部模块,主要用于组织代码,避免命名冲突
  2. 模块:TS的外部模块简称,侧重代码的复用,一个模块里可能会有多个命名空间。
namespace A {
    interface Animal {
    name:string;
    eat(str:string):void
    }
    export class Dog implement Animal {
    name:string;
    constructor(name:string){
    this.name = name
    }
         eat(){
        console.log(this.name + '吃肉肉')
    }
}

const d = new A.Dog('小黑')
d.eat()

装饰器(Class Decorator)

能够帮助我们来注释或者是修改代码的行为-这钟做法我们通常称为元编程。

装饰器能够很好的抽象代码,它们最合适用来包装可能会多处复用的逻辑

装饰器工厂可以传参

装饰器工厂模式。我们将装饰器本身封装在另外一个函数中,这样就能给装饰器传递变量了,例如 @Cool('stuff')。而当你不想给装饰器传参,把外层那个函数去掉就好了 @Cool

function logClass(params:string){ // 装饰器工厂
  return function(target:any){ //装饰器
    console.log(target)
    console.log(params)
    target.prototype.apiUrl = params
  }
}
@logClass('hello')
class HttpClient{
  constructor(){
    
  }
  getData(){
    
  }
}

装饰器的重载类(构造函数、方法)

类装饰器使得开发者能够拦截类的构造方法

function logClass(target:any){
  console.log(target)
  return class extends target{
    apiUrl:any = '我是修改后的数据'
    
    getData(){
      this.apiUrl + '-----'
      console.log(this.apiUrl)
    }
  }
}

@logClass
class HttpClient{
  public apiUrl: string | undfined;
  constructor(){
    this.apiUrl = '我是构造函数的apiUrl'
  }
  getData(){
    console.log(this.apiUrl)
  }
}

属性装饰器

属性装饰器表达式会在运行时当作函数被调用,转入下列2个参数;

  1. 原型对象
  2. 当前属性的名称
function logProperty(params:any){
  return function(target:any, propname: any){//1.原型对象 2.当前属性的名称
    console.log(target)
    console.log(propname)
    target[propname] = params
  }
}
function logClass(params:string){
  return function(target:any){
    console.log(target)
    console.log(params)
    target.prototype.apiUrl = params
  }
}
@logClass(‘xxxx’)
class HttpClient{
  @logProperty('http://hahaha')
  public Url: any | undfined;
  constructor(){
    this.apiUrl = '我是构造函数的apiUrl'
  }
  getData(){
    console.log(this.Url)
  }
}

// 属性装饰器就可以使得我们在对属性赋值之前或在取属性之后附加一些操作,就像中间件那样
export class IceCreamComponent {
  @Emoji()
  flavor = 'vanilla'
}

function Emoji(target: Object, key: string | symbol) {
  let value = target[key];
  const getter = () => {
    return value;
  }
    const setter = (next) => {
    console.log('改变')
    value = next
  }
  Object.defineProperty(target, key , {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  })
}

方法装饰器

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

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

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字
  3. 成员的属性描述对象(6个)
function (params:any) {
  return function(target:any, methodsName:string, desc:any){
    //修改装饰器方法,把装饰器方法里面传入的所有参数改为string类型
    //1.保存当前的方法
    const oldMethod = desc.value;
    desc.value = function(...args:any[]){
      args = args.map((value)=>{
        return String(value);
      })
       oMethod.apply(this, args);
    }
  }
}

class HttpClient {
    public url:any|undefined
  constructor(){
    
  }
  @logMethod
  getDate(...args:any[]){
    console.log(this.url)
  }
}
export class IceCreamComponent {

  toppings = [];

  @Confirmable('Are you sure?')
  @Confirmable('Are you super, super sure? There is no going back!')
  addTopping(topping) {
    this.toppings.push(topping);
  }

}


// Method Decorator
function Confirmable(message: string) {
  return function (target: Object, key: string | symbol, descriptor: PropertyDescriptor) {
    const original = descriptor.value;

      descriptor.value = function( ... args: any[]) {
          const allow = confirm(message);

          if (allow) {
            const result = original.apply(this, args);
            return result;
          } else {
            return null;
          }
    };

    return descriptor;
  };
}

方法参数装饰器

可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 参数的名字
  3. 参数在函数参数列表中的索引
function logParams(parms:any){
  return function(target:any, methodName:any,paramsIndex:any){
    console.log(target)
    
    }
}
class HttpClient{
  public url:any|undefind;
  constructor(){
    
  }
  getData(@logParams('uuid') uuid:any){
    console.log(uuid)
  }
}

const http = new HttpClient()
http.getData(123456)

装饰器的执行顺序

  • 如果同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。复合函数的形式。

理解为包裹从同一类型装饰器,从后往前

属性>>方法>>方法参数>>类装饰器

  1. 属性装饰器
  2. 方法装饰器
  3. 方法参数装饰器
  4. 类装饰器

普通函数-高阶函数的形式实现

function doSomething(name) {
  console.log('Hello, ' + name);
}

function loggingDecorator(wrapped) {
  return function() {
    console.log('Starting');
    const result = wrapped.call(this, ...arguments);
    console.log('Finished');
    return result;
  }
}

const wrapped = loggingDecorator(doSomething);
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。