• TS:知识补充


    .d.ts文件是ts用来声明变量,模块,type,interface等# 前言
    在项目中ts用到的不多顶多就是用来定义类型。但是慢慢发现有很多比较重要的知识点,起始是不知道的。写这篇文章是用来对自己的ts进行知识补充。

    .d.ts和declare

    .d.ts

    • .d.ts文件是ts用来声明变量,模块,type,interface等内容的
    • .d.ts声明变量或者模块等东西之后,在其他地方可以不用import导入这些东西就可以直接使用,而且有语法提示。
    • 创建了.d.ts文件还需要在tsconfig.json文件里面的include数组里面添加这个文件。include数组里面可以不用写.d.ts文件的绝对路径,可以通过glob通配符,匹配这个文件所在的文件夹或者是“祖宗级别”文件夹。
    • 支持的glob通配符有:
        • *:匹配0或多个字符(不包括目录分隔符)
        • ?:匹配一个任意字符(不包括目录分隔符)
        • **/:递归匹配任意子目录

    declare

    • .d.ts 文件中的顶级声明必须以 declareexport 修饰符开头。
    • 通过declare声明的类型或者变量或者模块,在include包含的文件范围内,都可以直接引用而不用去import或者import type相应的变量或者类型。
    • d.ts文件顶级声明declare最好不要跟export同级使用,不然在其他ts引用这个.d.ts的内容的时候,就需要手动import导入了
    • 声明的方法或者类里面的方法是不能有具体的实现的。

    示例

    定义一个my.d.ts 文件,并在tsconfig.json里引入

     "include": ["src/**/*.d.ts"],
    
    • 1

    定义

    //声明一个自定义类型Person
    declare type Person = {
      age: number;
      name: string;
    };
    
    //声明方法
    declare function add(num1: number, num2: number): number;
    
    // 声明class类
    declare class Dog {
      color: string;
      constructor(color: string);
    
      getDogColor(): string;
    }
    
    //声明命名空间,解决命名冲突问题,我们可以在不同命名空间中定义同名的变量
    declare namespace s1 {
      let name: string;
      function func(str: string): string;
    }
    
    //混合类型,比如jQuery
    declare let jQuery: (selector: string) => any;
    
    //声明模块
    declare namespace abcd {
      export let a: number;
      export function b(): void;
      export namespace c {
        let a: number;
      }
    }
    
    • 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

    使用

    <script setup lang="ts">
    // 自定义类型的使用
    const person: Person = {
        age: 5,
        name: '这是自定义类型'
    };
    console.log('自定义类型:', person.name);
    
    // 声明的方法
    const myAdd: add = (a: number, b: number) => {
        return a + b;
    };
    console.log('方法:', myAdd(1, 2));
    
    // 声明的变量
    console.log('变量:', aa);
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    个人理解就是用来规范格式的,这样可以在ts文件中直接使用。等同于写一个普通的ts文件,将这些规范导出,使用时再导入

    装饰器

    参考:https://www.tslang.cn/docs/handbook/decorators.html

    定义

    装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。

    基本概念

    如果要使用装饰器必须在tsconfig.json里启用experimentalDecorators编译器选项:

    {
        "compilerOptions": {
            "target": "ES5",
            "experimentalDecorators": true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    装饰器工厂
    如果我们要定制一个装饰器并且应用到一个声明上,我们得写一个装饰器工厂函数。 装饰器工厂就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。

    function color(value: string) { // 这是一个装饰器工厂
        return function (target) { //  这是装饰器
            // do something with "target" and "value"...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    装饰器组合

    多个装饰器可以同时应用到一个声明上。

    • 书写在同一行上:
    @f @g x
    
    • 1
    • 书写在多行上:
    @f
    @g
    x
    
    • 1
    • 2
    • 3

    当多个装饰器应用在一个声明上时会进行如下步骤的操作:

    • 由上至下依次对装饰器表达式求值。
    • 求值的结果会被当作函数,由下至上依次调用。

    简单使用

    类装饰器

    类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)。

    类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

    如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

    function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
        return class extends constructor {
            newProperty = 'new property';
            hello = 'override';
        };
    }
    
    @classDecorator
    class Greeter {
        property = 'property';
        hello: string;
        constructor(m: string) {
            this.hello = m;
        }
    }
    
    console.log(new Greeter('world'));
    
    //或者
    const doc: ClassDecorator = (target: any) => {
      target.newProperty = 'new property';
      target.hello = 'override';
    };
    
    @doc
    class Greeter {
      property = 'property';
      hello: string;
      constructor(m: string) {
        this.hello = m;
      }
    }
    
    console.log(new Greeter('world'));
    
    • 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

    执行结果如下,可以看到不存在的属性会被添加,相同的属性会被更改。这样做的好处是我们可以不用破坏类的原有结构来添加一些属性和方法。
    在这里插入图片描述

    方法装饰器

    方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。

    方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

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

    注意:

    • 如果代码输出目标版本小于ES5,属性描述符将会是undefined。
    • 如果方法装饰器返回一个值,它会被用作方法的属性描述符。
    class Greeter {
        greeting: string;
        constructor(message: string) {
            this.greeting = message;
        }
    
        @enumerable(false)
        greet() {
            return 'Hello, ' + this.greeting;
        }
    }
    function enumerable(value: boolean) {
        return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
            console.log('target:', target);
            console.log('propertyKey:', propertyKey);
            console.log('descriptor', descriptor);
            descriptor.enumerable = value;
        };
    }
    
    console.log(new Greeter('world'));
    
    //或者
    class Greeter {
      greeting: string;
      constructor(message: string) {
        this.greeting = message;
      }
    
      @enumerable(false)
      greet() {
        return 'Hello, ' + this.greeting;
      }
    }
    const enumerable = (value: boolean): MethodDecorator => {
      return function (
        target: any,
        propertyKey: string,
        descriptor: PropertyDescriptor,
      ) {
        console.log('target:', target);
        console.log('propertyKey:', propertyKey);
        console.log('descriptor', descriptor);
        descriptor.enumerable = value;
      };
    };
    
    console.log(new Greeter('world'));
    
    • 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

    访问器装饰器

    访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

    访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

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

    注意:

    • 如果代码输出目标版本小于ES5,Property Descriptor将会是undefined。
    • TypeScript不允许同时装饰一个成员的get和set访问器。
    class Point {
        private _x: number;
        private _y: number;
        constructor(x: number, y: number) {
            this._x = x;
            this._y = y;
        }
    
        @configurable(false)
        get x() { return this._x; }
    
        @configurable(false)
        get y() { return this._y; }
    }
    
    function configurable(value: boolean) {
        return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
            descriptor.configurable = value;
        };
    }
    
    console.log(new Point(1, 2));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    属性装饰器
    官方说目前只能用来监视类中是否声明了某个名字的属性。感觉没啥用,略。

    参数装饰器
    官方说 参数装饰器只能用来监视一个方法的参数是否被传入。感觉也没啥用,略。

    demo,实现一个GET请求

    案例来源:小满nestjs(第四章 前置知识装饰器-实现一个GET请求)

    哎,发现自己好菜啊。

    import axios from 'axios';
    
    const Get = (url: string): MethodDecorator => {
        return (target, key, descriptor: PropertyDescriptor) => {
            // 这里的fnc就是getList,这样讲get请求的返回值又作为参数传给了getList函数
            const fnc = descriptor.value;
            axios
                .get(url)
                .then((res) => {
                    fnc(res, {
                        status: 200
                    });
                })
                .catch((e) => {
                    fnc(e, {
                        status: 500
                    });
                });
        };
    };
    
    // 定义控制器
    class Controller {
        constructor() { }
        @Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
        getList(res: any, status: any) {
            return {
                list: res.data.result.list, status
            };
        }
    }
    
    • 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
  • 相关阅读:
    第二次面试 9.15
    nodejs基于微信小程序的图书销售商城系统 uniapp 小程序
    Hive矢量化
    怎么压缩PDF文件大小?分享几个压缩文件的方法
    矩阵分析与应用
    分布式调度框架Elastic-Job和xxl-job区别
    Matlab:Matlab编程语言应用之数学统计(柱状图、曲线分析等)的使用方法简介、案例实现之详细攻略
    Linux部署Tomcat踩的坑以及解决方案【8080无法访问、日志显示XX端口被占用、修改默认端口、无法提供安全连接】
    小程序商城上线需要做什么?
    自动编码器简单理解及简单使用描述
  • 原文地址:https://blog.csdn.net/weixin_41897680/article/details/127948971