首页 > 其他分享 >NestJS 筑基:TypeScript 类和装饰器

NestJS 筑基:TypeScript 类和装饰器

时间:2023-12-10 21:33:06浏览次数:29  
标签:TypeScript name age 筑基 class NestJS 装饰 string 属性

前言

先回顾下前文中介绍了哪些内容:

  • 使用 @nestjs/cli 创建和管理 Nest 应用
  • Hello, World 示例代码分析
  • Nest 基本概念:模块,控制器,服务
  • 常用的装饰器:@Module、@Controller、@Get、@Injectable
  • Nest 目录结构分析
  • @nest/cli 脚手架的命令

本文先不继续讲解 Nest 中的内容,而是打算介绍 TypeScript 中的两个语法:类和装饰器,帮助新手理解 Nest 中代码的写法。

如果你对 TypeScript 已经很熟悉,根据自己实际情况有选择的阅读即可。

类 Class

NestJS 支持面向对象编程,也支持函数式编程。但在实际开发中还是以面向对象为主,而面向对象又是和类紧密联系的,所以对于类的一些概念和语法,一定要熟练掌握。

ES6 推出了 class 类,本质是过去的构造函数的语法糖。TypeScript 中的类的用法和 ES 标准中的类大差不离,多了一些更加 OOP 的语法支持。、

下面是对类的一些常用语法的说明。

类成员

在 JS 中类的成员有两种,分别是成员属性成员方法。TypeScript 官方文档中会称之为字段(filed)和方法(method)。本文描述时会按照 JS 的习惯。

属性

在 TS 中声明类使用 class 关键字,如果实例有属性,则必须先声明其属性类型。如下,声明一个 Person 类:

class Person {
  // 声明类的属性,可以使用类型注解声明类型
  name: string;
  age: number;
    
  // 也可以省略,则默认是 any 类型
  address;
}

// 实例化
const person = new Person('kw', 18);
console.log(person.name);
初始值

类的属性可以设置初始值,既可以在声明时设置,也可以在构造函数中设置:

class Person {
  name: string = 'kw';
  age: number = 18;
}

class Person { 
  name: string;
  age: number;
    
  constructor() {
      this.name = 'kw';
      this.age = 18;
  }
}
--strictPropertyInitialization

这是 tsconfig.json 中的一个配置项,是否严格检查类的属性初始化操作。默认为 true,表示类的成员属性必须在声明时或者在构造函数中进行初始化操作。如果该选项开启了:

class Person { 
  // 正确,在声明时完成了初始化
  name: string = 'kw';
  // 正确,在构造函数中完成了初始化
  age: number;
  // 报错:Property 'address' has no initializer and is not definitely assigned in the constructor.
  address: string;
    
  constructor() {
      this.age = 18;
  }
}

有些场景下需要在别的地方进行属性的初始化,此时可以对属性应用非空断言:

class Person {
    name!: string;
}

此时虽然没有做初始化,但是编译器也不会报错了。

readonly 修饰符

使用 readonly 修饰类的成员属性后,该属性就变为了只读属性,只能读,不能修改。因此对于只读属性,必须在声明时,或者在构造函数中进行初始化,否则会报错。

class Person { 
  readonly name: string = 'kw';
  age: number;
}
构造方法

构造方法用于创建和初始化类的实例对象。使用 new 操作一个类时,就会触发这个类的构造方法的执行。

构造方法不是必须的,类有一个默认的空的构造方法。但是如果需要为类的实例设置不同的属性,则必须实现一个构造方法。

class Person { 
  name: string;
  age: number;

    
  constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
  }
}

// 实例化
const person = new Person();
console.log(person.name);
console.log(person.age);

可访问性修饰符

修饰符用来限制类的成员属性和成员方法的可访问范围

假设有一个 A 类和继承自 A 的 B 类,则可见范围有三种:任意地方,A 类内部,B 类的内部。分别对应了三种修饰符:

  • public:默认的修饰符,表示成员是公有的,可在任意地方被访问
  • protected:受保护的修饰符,表示成员能在当前类的内部,子类的内部被访问到
  • private:私有修饰符,表示成员只能在类的内部被访问到

NestJS 筑基:TypeScript 类和装饰器_Node.js

class A {
    // 默认为 public,等同于 public a
    a;
    // 受保护的属性,可以在当前类和子类中访问
    protected b;
    // 私有属性,只能在当前类内部访问
    private c;
    
    test() {
        console.log(this.c)
    }
}

class B extends A {
    test() {
        console.log(this.b)
    }
}

参数属性

参数属性(Parameter Properties)是一种能简化代码的语法糖。例如要声明一个类,要先声明成员属性,再写构造方法进行初始化:

class Person { 
  name: string;
  age: number;

    
  constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
  }
}

const person = new Person('kw', 18)
console.log(person.name)
console.log(person.age)

当这个类有非常多的属性时,光这些初始化代码可能就要写几十行。

在 TypeScript 中,在构造函数的参数前加上一个可见性修饰符publicprivateprotected 或者 readonly,该参数就变为了参数属性,TypeScript 会将这些构造函数的参数转换为具有相同名称和值的类属性

上面的 Person 类就等同于这种写法:

class Person { 
  // 在声明类成员时 public 修饰符可以省略,但在使用参数属性时,不能省略
  constructor(public name: string, public age: number) {
    // 不需要函数体
  }
}

const person = new Person('kw', 18)
console.log(person.name)
console.log(person.age)

回顾下 AppController 控制器的代码:

export class AppController {
  constructor(private readonly appService: AppService) {}
}

现在再看这段代码是什么含义就很清晰了,它的完整写法是:

export class AppController {
  // appService 是私有属性且只读,需要在声明或者构造函数中完成初始化
  private readonly appService: AppService;
    
  constructor(appService: AppService) {
      this.appService = appService;
  }
}

类的其他语法

类还有一些其他语法,比如访问器,静态成员属性和方法,继承等等。这些内容如果还不熟悉,可以阅读其他文章博客或者文档,我们先学习上面这些,够用为主。

装饰器 Decorator

装饰器,看名字就知道是用起装饰作用的。它用来增强类(class)的功能,许多面向对象的语言都有这种语法。

虽然装饰器的历史悠久,但是由于 JS 这门语言本身很少有场景需要使用到装饰器,所以目前装饰器仍处于提案阶段,并且这一提,就是很多年。记得最早在 ES2015 中,装饰器语法就已经处于提案阶段了。

虽然 ES 标准的装饰器还未成为标准,但是 TS 中的装饰器可以大胆使用。

装饰器的本质就是函数,只不过使用形式上和普通函数有所不同:普通函数使用 () 调用,装饰器使用 @ 调用。普通函数可以在任意位置执行,装饰器只能用在类和类的成员身上

装饰器和装饰器工厂

先来看一个装饰器的最简单的例子。

function Fn(target: any) {
  let a = new target;
  a.say()
}

@Fn
class A {
  say() {
    console.log('hello')
  }
}

代码执行,打印 “hello”。

Fn 是一个装饰器函数,当被用在类上时,它所接收的参数就是该类。所以可以在装饰器内部,实例化 A 类型,并调用 a对象 的实例方法。

如果需要一些定制化的内容,想让装饰器接收一些其他的参数,就要用到装饰器工厂了。装饰器工厂就是一个返回装饰器的工厂函数。比如:

function FnFactory(name: string) {
    
  // 返回一个装饰器
  return function(target: any) {
      let a = new target;
      a.say(name)
  }
  
}

@FnFactory('kw')
class A {
  say(name: string) {
    console.log('hello, ', name)
  }
}

代码执行,打印 “hello, kw”。

注意区分装饰器和装饰器工厂的使用,普通的装饰器的用法是 @装饰器,装饰器工厂的用法需要带上圆括号,@装饰器()

类装饰器

根据所修饰对象的不同,装饰器具体可以分为:

  • 类装饰器
  • 属性装饰器
  • 方法装饰器
  • 存取器装饰器
  • 参数装饰器

上面的示例中,装饰器在类上调用,这就是类装饰器。类装饰器只有一个参数,就是被装饰的类

属性装饰器

应用在类的属性成员上的装饰器,可接收两个参数:

  • 如果是静态属性,则是类的构造方法;如果是实例属性,则是类的原型对象
  • 属性名
function Property() {
  return function (target: any, property: any) {
    console.log(target)
    console.log(property)
  }
}

class C {
  @Property()
  name!:string;
}

// {}
// name

方法装饰器

方法装饰器用的比较多,它接收3个参数:

  • 如果修饰的是静态成员,则为类的构造函数;如果修饰的是实例方法,则为类的原型对象
  • 方法名
  • 方法的属性描述符
function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

class Greeter {
  greeting: string;
    
  constructor(message: string) {
    this.greeting = message;
  }
 
  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

这个装饰器的作用是修改属性描述符的 enumerable 属性,设为 false,也就代表着该方法不能被枚举了。

存取器装饰器

用在访问器身上的装饰器,可接收参数和方法装饰器相同。

function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value;
  };
}

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;
  }
}

该装饰器可以动态修改存取器的属性描述符的 configurable 属性,示例中将其设置为 false,表示不能被删除,也不能被修改。

参数装饰器

应用在构造方法或者实例方法的参数上的装饰器,它接收三个参数:

  • 如果修饰的是静态成员方法,则为类的构造函数;如果修饰的是构造方法或者实例方法,则为类的原型对象
  • 参数所在方法的名字
  • 参数在原函数的参数列表中的位置,也就是第几个参数
// 参数装饰器
function Param() {
  return function(target: any, param: string, index: number) {
    console.log(target)
    console.log(param)
    console.log(index)
  }
}

class Person {
  say(@Param() msg:string) {
    console.log(msg)
  }
}

// {}
// say
// 0

@Controller 和 @Get 装饰器

看了这么多装饰器,已经眼花缭乱了,来看一个实际的例子。

打开 app.controller.ts,修改为以下内容:

import { Controller, Get, Query } from '@nestjs/common';
import { AppService } from './app.service';

// 声明类为控制器,并为该模块的路由设置一个请求前缀 news
@Controller('news')
export class AppController {
  constructor(private readonly appService: AppService) {}

  // 和路由前缀拼接,处理 Get /news/list 请求
  @Get('list')
  // 参数装饰器,入参是请求中的 query 参数
  getHello(@Query('page') page) {
    return {
      code: 0,
      data: {
        list: [],
        page,
      },
    };
  }
}

打开浏览器,访问 localhost:3000/news/list?page=10

NestJS 筑基:TypeScript 类和装饰器_TypeScript_02

当然,Nest 中还有非常多的装饰器,后面也会继续介绍。

总结

本文没有继续介绍 NestJS 有关的内容,而是讲解了 TypeScript 中两个很重要的语法:类和装饰器,可以帮助新手理解 Nest 应用中的一些代码写法的含义。

毕竟不积跬步,无以至千里,打好这些基础,后面上手的也会更顺利。

标签:TypeScript,name,age,筑基,class,NestJS,装饰,string,属性
From: https://blog.51cto.com/u_12382805/8762647

相关文章

  • TypeScript(1)
    1.数据类型1.1基本数据类型consta:number=1constb:string='123'constc:boolean=true//undefinedandnullbelongstoothertypesconstd:null=nullconste:undefined=undefined1.2引用数据类型consta:number[]=[]//allelements......
  • 深度掌握TypeScript中的重载【函数重载、方法重载】
    深度掌握TypeScript中的重载【函数重载、方法重载】1.函数重载,方法重载的重要性著名前端流行框架底层都用到函数重载,例如:Vue3底层源码就多处使用到带泛型的函数重载。很多前端面试更是拿函数重载作为考核求职者TS技能是否扎实的标准之一,如果你不掌握函数重载,等于你的TS技......
  • Vite4+Typescript+Vue3+Pinia 从零搭建(6) - 状态管理pina
    项目代码同步至码云weiz-vue3-templatepina是vue3官方推荐的状态管理库,由Vue核心团队维护,旨在替代vuex。pina的更多介绍,可从pina官网查看特点更简洁直接的API,提供组合式风格的API支持模块热更新和服务端渲染对TS支持更为友好安装npmipinia使用1.创建......
  • 微信小程序 wx.request Typescript 封装统一请求
    话不多说直接上代码,想懂的终究会懂,哈哈哈哈文件名:request.ts 1/**2*HttpMethod类型api处要用3*/4exportenumHttpMethod{5Get="GET",6Post="POST",7Options="OPTIONS",8Put="PUT",9Delete=......
  • TypeScript中的类
    TypeScript类1.TypeScript中类的意义​ 相对以前JavaScript不得不用构造函数来充当”类“,TypeScript类的出现可以说是一次技术革命。让开发出来的项目尤其是大中项目的可读性好,可扩展性好了不是一点半点。​ TypeScrip类的出现完全改变了前端领域项目代码编写模式,配合......
  • 1.TypeScript安装
    TypeScript是由微软开发的一款开源的编程语言。它是JavaScript的超级,扩展了JavaScript的语法,遵循最新的ES6、ES5规范。TypeScript更像后端java、C#这样的面向对象语言可以让js开发大型企业项目。安装TypeScriptnpminstall-gtypescript//安装命令tschelloworld.ts......
  • 初始NestJS
    根据官网所说Nest(NestJS)是一个用于构建高效、可扩展Node.js服务器端应用程序的框架。它使用渐进式的JavaScript,完全支持并构建于TypeScript上(同时仍然允许开发者使用纯JavaScript进行编码),结合了面向对象编程(OOP)、函数式编程(FP)和函数响应式编程(FRP)的元素。那么我们先看看Nest......
  • react18 typeScript useSelector 提示state 类型未知
     解决方案store/index.tsximport{configureStore}from'@reduxjs/toolkit';import{useSelector,useDispatch}from'react-redux'importtype{TypedUseSelectorHook}from'react-redux'importuserfrom'./modules/user&#......
  • TypeScript学习(1)
    TS基础基本用法TypeScript代码最明显的特征,就是为JavaScript变量加上了类型声明。letfoo:string;变量foo的后面使用冒号,声明了它的类型为string。类型声明的写法,一律为在标识符后面添加“冒号+类型”。函数参数和返回值,也是这样来声明类型。functiontoString(num:nu......
  • Vite4+Typescript+Vue3+Pinia 从零搭建(5) - 路由router
    项目代码同步至码云weiz-vue3-templateVueRouter是Vue.js的官方路由。它与Vue.js核心深度集成,让用Vue.js构建单页应用变得轻而易举。1.安装npmivue-router@42.集成1.新建两页面进行示例在src/view下新建home.vue和login.vue,内容如下:<scriptsetuplang="t......