1-2 TypeScript的定义?
Typescript 是 JavaScript 的超集,它有自己的静态类型
Typescript 不能直接在浏览器或者 Node 环境下运行,需要编译为普通 js 才能执行
// 静态类型 ts中所说的类型,指的就是静态类型
let b: number = 123;
b = 456;
// 动态类型, a 的值随时可变
// let a = 123;
// a = '456'
1-3 TypeScript 带来了什么优势?
开发过程中发现潜在的问题
编辑器中能更好的提示(代码自动补充全,错误提示)
代码语义更清晰易懂,可读性更好(直观看到代码语义)
// type Point = { x: number, y: number }
interface Point { x: number, y: number }
function tsDemo(data: Point) {
return Math.sqrt(data.x ** 2 + data.y);
}
tsDemo({ x: 1, y: 123 });
1-4 TypeScript基础环境搭建
VSCode编辑器设置
编辑器设置搜索 quote, 找到 Typescript 类别, 在 Quote Style 中选择 single
编辑器设置搜索 tab, 选择缩进为2个空格
Format On Save: true
Prettier: Single Quote : true
安装 Typescript
npm install -g typescript
编译成普通 js
tsc 文件名
执行编译后的 js 的运行结果
前面说了Typescript文件不能直接在浏览器或者node下运行,如果需要将TypeScript文件直接运行,可以安装ts-node
npm install -g ts-node
然后运行ts-node script.ts 命令
1-5 静态类型的深度理解
我们看到一个变量它是静态类型,不仅仅意味着这个变量的类型不能修改,还意味着这个变量上的属性和方法基本也就确定了
const count: number = 123 // count. 的时候下面所有属性都是number所拥有的
interface Point {
x: number
y: number
}
const count: Point = {
x: 2,
y: 4,
}
// 变量 count 是一个自定义 Point 静态类型, count 具备 Point 类型上的所有属性和方法
1-6 基础类型和对象类型
静态类型的优点: 可以更直观的判断变量或者属性的内容是什么
基础类型 number string bloean void undefind symbol null ...
const number: number = 123
const teacherName: string = 'Dell'
对象类型
const teacher: {
name: String
age: number
} = {
name: 'TangShuo',
age: 18,
}
数组类型
const numbers: number[] = [1, 2, 3]
Class 类型
class Person {}
// dell 必须是一个Person的类
const dell: Person = new Person()
函数类型, 它的返回值是数字类型
const getTotal: () => number = () => {
return 123
}
// 等号前面是类型注解, 等号后面是函数具体实现
1-7 类型注解和类型推断
type annotation 类型注解, 我们来告诉 TS 变量是什么类型
type inference 类型推断, TS 会自动的去尝试分析变量的类型
如果 TS 能够自动分析变量类型,我们就什么也不需要做了
如果 TS 无法分析变量类型的话,我们就需要使用类型注解
使用方法
如果TS能自动分析变量类型,那么我们什么都不用做了,例如:
const firstNumber = 123
const secondNumber = 456
const total = firstNumber + secondNumber
// 这儿的 total 变量能自动分析为 number 类型
如果TS无法分析变量类型, 我们就需要使用类型注释 例如:
function getTotal(firstNumber: number, secondNumber: number): number {
return firstNumber + secondNumber
}
const total = getTotal(1, 2)
// getTotal 函数的返回值要加上 number 类型, 因为 return 的可以是其它类型, 例如 return firstNumber + secondNumber + ''
1-8 函数相关类型
函数类型示例
// 入参即形参
function add(first: number, second: number): number {
return first + second
}
const total = add(1, 2)
为什么要加上函数返回值的类型,因为 return 语句中可能返回其他的数据类型,例如:
function add(first: number, second: number): number {
return first + second + ''
}
const total2 = add(1, 2)
函数返回值 void, void 是空的意思, 加在函数上的意思是--我希望这个函数它没有返回值
function sayHello(): void {
console.log('Hello')
}
函数返回值 never, never 永远不可能执行到最后
function throwError(): never {
throw new Error()
console.log('123') // 这里的代码不会执行
}
throwError()
函数参数解构语法的类型注解
// 只要是解构语法, 类型注解必须写在花括号里
function add({ first, second }: { first: number; second: number }): number {
return first + second
}
add({ first: 1, second: 2 })
1-9 基础语法复习
基础类型
// 基础类型, boolean, number, string, void, undefined, symbol, null
let count: number
count = 123
对象类型, {}, Class, function, []
// 函数参数和返回值类型注解
// 这儿的 :number 类型其实不用写, 因为 parseInt 方法可以直接推断出来
const func = (str: string): number => {
return parseInt(str, 10)
}
// 这儿的 number 类型一定要写, 因为不写语法就不对了
const func1: (str: string) => number = (str) => {
return parseInt(str, 10)
}
// 接受类型参数->返回类型值->返回值
// 巧记: 等号前面是参数返回类型, 等号后面后面是函数体
// 经验: 函数的参数往往需要类型注解,函数的返回值可以通过类型推断推断出来
其他类型
const date: Date = new Date()
变量有可能是多种类型的情况
// 联合类型
let temp: number | string = 123
temp = '456'
1-10 数组和元组
数组
const numberArr: number[] = [1, 2, 3]
// 表示 arrNumber 变量是一个数组,数组的每一项都是 number 类型
const stringArr: string[] = ['a', 'b', 'c'];
const stringArrOrNumberArrarr: (string | number)[] = [1, 'hello', 2]
数组存储对象类型的注解该怎么写?
const objectArr: { name: string; age: number }[] = [
{
name: 'dell',
age: 18,
},
]
// 类型别名, 将类型放到数组前面
type User = { name: string; age: number }
const objectArr1: User[] = [
{
name: 'dell',
age: 18,
},
]
元组
概念: 一个数组他的长度是固定的,他每一项的类型也是固定的
// 元组 tuple 约束数组每一项的类型
const teacherInfo: [string, string, number] = ['Dell', 'male', 28]
// csv
const teacherList: [string, string, number][] = [['dell', 'male', 19], ['sun', 'female', 26], ['jeny', 'female', 38]];
1-11 interface
一些通用的类型集合我们可以用 interface 去表示出来
interface 和 type 的区别, type 可以直接代表一个string, 而interface只能代表对象或函数
type Person = string
interface Person {
name: 'tangshuo'
}
// interface 和 type 很类似但是又有区别
// interface 只能代表一个对象或者一个函数, type Person1 = string 可以直接代表一个基础类型
interface Person {
readonly name: string // readonly 表示这个属性只能读,不能写
job: string
age?: number // 加问号代表属性可有可无
[propName: string]: any // 额外属性用 propName
say(): string // 方法
}
// 一个接口继承另外一个接口
interface Teacher extends Person {
thacher(): string
}
// 函数使用接口
interface SayHi {
(word: string): string;
}
// type
type Person1 = {
name: string
}
const getPersonalName = (person: Person) => {
console.log(person.name)
}
const setPersonalName = (person: Teacher, name: string) => {
person.name = name
}
const person = {
name: 'dell',
sex: 'male',
say() {
return 'hello'
},
teacher() {
return 'teacher'
},
}
getPersonalName(person)
getPersonalName({
name: 'dell',
sex: 'male',
say() {
return 'hello'
},
teacher() {
return 'teacher'
},
})
// 以对象文字传递的时候会进行强校验
setPersonalName(person, 'tangshuo')
const say: SayHi = (word: string) => {
return word
}
// 一个类想使用接口做约束的时候,使用 implements 语法
class User implements Person {
name = 'dell'
say() {
return 'hello'
}
}
// 将 ts编译成原生的 js改怎么做
// tsc demo.ts 接口的作用就是在开发过程中帮助我们做语法提示的工具,编译之后会剔除掉接口这些内容
1-12 类的定义与继承
class Person {
name = 'tangshuo'
getName() {
console.log(this.name)
return this.name
}
}
class Teacher extends Person {
getTeacherName() {
return 'Dell Lee'
}
getName() {
// 重写父类的方法并调用父类的方法
return super.getName() + 'Hello'
}
}
const teachers = new Teacher()
console.log(teachers.getName())
console.log(teachers.getTeacherName())
1-13 类中的访问类型和构造器
// private, protected, public 访问类型
// public 允许在类的内外调用
// private 允许在类的内部被使用
// protected 允许在类内以及继承的子类中使用
// class Person {
// public name: string
// // private name: string
// sayHi() {
// console.log('Hi')
// }
// }
// const person3 = new Person()
// person3.name = 'Dell Lee'
// console.log(person3.name)
class Person {
// public name: string
// constructor(name) {
// this.name = name
// }
// 在构造函数的参数上加上 public 等价于 public name: string + this.name = name 操作
constructor(public name: string) {}
}
const person4 = new Person('Dell')
console.log(person4.name)
class Teacher extends Person {
constructor(public age: number) {
// super() 指的是调用父类的构造函数
// 调用父类的方法还要把参数传进去
super('dell')
}
}
const teacher = new Teacher(28)
01-14.静态属性,Setter和Getter
getter and setter
// 类的实例如何访问私有属性?
class Person {
constructor(private _name: string) {}
get name() {
return this._name + ' lee'
}
set name(name: string) {
const realName = name.split(' ')[0]
this._name = realName
}
}
const person = new Person('dell')
console.log(person.name)
person.name = 'dell lee'
console.log(person.name)
用Typescript实现一个单例模式(一个类只允许获取一个这个类的实例)
class Demo {
private static instance: Demo
private constructor(public name: string) {}
// static 属于类的方法, 而不是属于类的实例上的方法
// public 可以不用写, 默认就是 public
public static getInstance() {
if (!this.instance) {
this.instance = new Demo('tangshuo')
}
return this.instance
}
}
const demo1 = Demo.getInstance()
const demo2 = Demo.getInstance()
console.log(demo1.name)
console.log(demo2.name)
// const demo2 = new Demo() // 类“Demo”的构造函数是私有的,仅可在类声明中访问, 不可能通过new Demo()的方式创建实例
01-15.抽象类.ts
抽象类是把类的一些公共(通用)的东西抽象出来, 而接口是把对象等一些东西抽离出来
// readonly
// class Person {
// public readonly name: string;
// constructor(name: string) {
// this.name = name;
// }
// }
// const person = new Person('Dell');
// person.name = 'hello';
// console.log(person.name);
// 抽象类 抽象类只能被继承,不能被实例化
// abstract class Geom {
// width: number;
// getType() {
// return 'Gemo';
// }
// abstract getArea(): number;
// }
// class Circle extends Geom {
// getArea() {
// return 123;
// }
// }
// class Square {}
// class Triangle {}
interface Person {
name: string;
}
interface Teacher extends Person {
teachingAge: number;
}
interface Student extends Person {
age: number;
}
interface Driver {
name: string;
age: number;
}
const teacher = {
name: 'dell',
teachingAge: 3
};
const student = {
name: 'lee',
age: 18
};
const getUserInfo = (user: Person) => {
console.log(user.name);
};
getUserInfo(teacher);
getUserInfo(student);
02-01.使用SuperAgent和类型定义文件获取页面内容
创建 package.json 文件 npm init -y
创建 tangshuo.config.json 文件 tsc --init
SuperAgent 的作用, 在 node 中发 ajax 请求获取页面内容
02-02.使用cheerio进行数据提取
cheerio: 为服务器特别定制的,快速、灵活、实施的jQuery核心实现
02-07.TypeScript的编译运转过程的进一步理解
将 ts 代码编译成 js 可使用 tsc 命令, 如果不指定路径的话会默认编译整个项目的 .ts 文件. 但是这样的话文件会很混乱, 应该将编译的文件放到一个指定文件夹下, 如 build 文件夹
实现上述功能可以配置 tsconfig.json 文件, 修改"outDir": "./"属性为 "outDir": "./build"
"scripts": {
"build": "tsc"
}
如果想实现不用每次修改代码都运行npm run build 命令, 可以将代码修改为
"scripts": {
"build": "tsc -w"
}
nodemon 的作用: 它将监视源中的任何更改并自动重新启动服务器
只要项目文件发生变化, 就自动运行./build/crowller.js这个文件, 注意 nodemon只能监测 js 文件的变化, 所以需要两个命令配合"build": "tsc -w",, 监听项目 ts 文件的变化, 一但变化就重新编译
注意他们两个各自的职责, 当两个命令都执行的时候,tsc -w监听到变化就会运行nodemon node ./build/crowller.js命令
问题: 现在需要运行两个命令, 有什么办法只运行一个命令呢? 还真有, concurrently来了,concurrently插件可同时运行多个命令 ,如 npm run watch-js & npm run watch-less
"scripts": {
"dev:build": "tsc -w",
"dev:start": "nodemon node ./build/crowller.js",
"dev": "concurrently npm:dev:*"
}
03-01.TypeScript中的配置文件
如果直接运行tsc demo.ts 是不会以tsconfig.json配置文件进行编译, 如果想要配置文件生效, 得直接运行 tsc命令, 不能带后面的参数
tsc默认编译根目录下的.ts文件,如果只想编译某个文件, 添加 include属性, 如
{
"include": ["./demo.ts"]
}
如果不想编译某个文件, 添加 `exclude' 属性
{
"exclude": ["./demo.ts"]
}
compilerOptions 编译配置文件的配置项
removeComments: true // 删除注释
noImplicitAny: false // 不要求显示的设置 any
strictNullChecks: false // 是否强制校验 null 类型 列如: const teacher: string = null, 如果为 true 的话就不能将string类型的变量赋值为null
rootDir: "./src" // ts 入口文件夹
outDir: "./build" // 编译后输出文件夹
incremental: true // 增量编译, 只编译 ts 文件后面新增的内容, 之前编译的内容不再编译
allowJs: true // 是否允许将项目中的 js 文件也进行编译
checkJs: true // typescript 是会对 ts 文件的语法进行检测的, 如果想要对 js 文件也进行语法检测, 可以设置为 true
noUnusedLocals: true // 对未使用的变量进行警告提示
noUnusedParameters: true // 对未使用的函数参数进行警告提示
baseUrl: "./" // ts 项目的根路径
outFile: // 指定一个文件,将所有输出捆绑到一个JavaScript文件中
请尝试将 “lib” 编译器选项更改为“es2017”或更高版本错误
// tsconfig.json
"compilerOptions": {
// ...
"lib": ["es2021"]
}
03-03.联合类型和类型保护
interface Bird {
fly: boolean
sing: () => {}
}
interface Dog {
fly: boolean
bark: () => {}
}
// 类型保护
// 类型断言的方式
function trainAnimal(animal: Bird | Dog) {
// 这儿就是联合类型, 使用 | 操作符
// 如果直接调用 sing 或 bark 方法, Typescript 就会报错, 因为不能保证传过来的对象一定有这两个方法
// 可以通过类型断言的方式来进行保护
if (animal.fly) {
;(animal as Bird).sing()
} else {
;(animal as Dog).bark()
}
}
// in 语法做类型保护
function trainAnimal2(animal: Bird | Dog) {
if ('sing' in animal) {
animal.sing()
} else {
animal.bark()
}
}
// typeof 语法做类型保护
function add(first: string | number, second: string | number) {
if (typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`
}
return first + second
}
// instanceof 语法做类型保护
class NumberObj {
count: number
}
function addSecond(first: string | NumberObj, second: string | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count
}
}
03-04.Enum 枚举类型
enum Status {
OFFLINE = 1,
ONLINE,
DELETED,
}
// 默认是从 0 开始的
console.log(Status.OFFLINE) // 1
console.log(Status.ONLINE) // 2
console.log(Status.DELETED) // 3
// 能反向映射, 根据数值反向去查枚举类型的名称
console.log(Status[1]) // OFFLINE
// const Status = {
// OFFLINE: 0,
// ONLINE: 1,
// DELETED: 2
// }
// enum的 应用场景, 接受几个固定的值返回固定的结果
function getResult(status) {
if (status === Status.OFFLINE) {
return 'offline'
} else if (status === Status.ONLINE) {
return 'online'
} else if (status === Status.DELETED) {
return 'deleted'
}
return 'error'
}
const result = getResult(1)
console.log(result)
03-05.函数泛型.ts
// 泛型 泛指的类型
// 使用: 在函数括号前面加一对尖括号, 里面跟一个或多个泛型, 泛型只在用的时候才知道是什么类型, 定义的时候并不知道
function join(first: string | number, second: string | number) {
return `${first}${second}`
}
// 现在需求是: 如果第一个参数传的是string类型, 第二个参数也必须是string类型, 如果第一个参数传的是number类型, 第二个参数也必须是number类型
function join2<T>(first: T, second: T) {
return `${first}${second}`
}
// 先定义一个泛型 T, 它是 string 还是 number 现在还不知道, 他可以指任何东西, 先让 first 等于这个泛型, second 也等于这个泛型
// 在使用函数的时候对这个泛型进行指定
// 翻译: 调用 join2 方法的时候, 我指定的泛型它的类型是string, 这个时候 T 就变成 string 了
join2<string>('1', '1')
join2<number>(1, 2)
function map<T>(params: T[]) {
// 等价于 params: Array<T>
return params
}
map<string>(['hello'])
// 如何设置多个泛型呢, 假如有一个需求, 第一个参数接受的是 number, 第二参数接受的是 string
// 可以使用多个泛型, 函数上面写了多个泛型的定义, 调用的时候把具体的类型定义下就可以了
function join3<T, P>(first: T, second: P) {
return `${first}${second}`
}
join3<number, string>(1, '2')
join3(1, '2') // 如果不写具体的定义的话, 他会根据你传入的参数进行推断具体类型是什么样子
// 可以让返回结果也等于泛型
function anotherJoin<T>(first: T, second: T): T {
return first
}
03-06.类中的泛型以及泛型类型
// 泛型能解决使用联合类型不灵活的问题
// class DataManager {
// constructor(private data: string[] | number[]) {}
// getItems(index: number){
// return this.data[index]
// }
// }
interface Item {
name: string
}
// 泛型必须拥有 Item 里面所有的东西
// 翻译: 我定义了一个泛型 T , 它是什么具体类型现在不知道, 他继承了类型 Item, 它未来会对应一个具体类型, 这个具体类型一定要有 Item 里所有的东西
class DataManager<T extends Item> {
constructor(private data: T[]) {}
getItems(index: number): string {
return this.data[index].name
}
}
// 实例化的时候泛型得到了具体的对应
const data = new DataManager<Item>([{ name: 'hello' }])
console.log(data.getItems(1))
// 通过 extends 对泛型进行约束
class DataManager2<T extends number | string> {
constructor(private data: T[]) {}
getItem(index: number): T {
return this.data[index]
}
}
const data2 = new DataManager2<string>([])
// 如何使用泛型作为一个具体的类型注解
function hello<T>(params: T) {
return params
}
const func: <T>(param: T) => T = hello
03-07.命名空间-namespace(上)
namespace Home {
class Header {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Header'
document.body.appendChild(elem)
}
}
class Content {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Content'
document.body.appendChild(elem)
}
}
class Footer {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Footer'
document.body.appendChild(elem)
}
}
export class Page {
constructor() {
new Header()
new Content()
new Footer()
}
}
}
<script>
new Home.Page()
</script>
03-08命名空间-namespace(下)
将 ts 文件编译后合并输出到一个文件 "outFile": "./dist/page.js", 使用这个配置必须设置 module 为 amd 或 system, 如: "module": "amd"
components.ts
namespace Components {
export interface User {
name: string
}
export class Header {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Header'
document.body.appendChild(elem)
}
}
export class Content {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Content'
document.body.appendChild(elem)
}
}
export class Footer {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Footer'
document.body.appendChild(elem)
}
}
}
page.ts
// namespace 相互引用的声明
/// <reference path="./components.ts" />
namespace Home {
export namespace Dell {
export const teacher: Components.User = {
name: 'dell',
}
}
export class Page {
constructor() {
new Components.Header()
new Components.Content()
new Components.Footer()
}
}
}
调用
<script src="./dist/page.js"></script>
<script>
new Home.Page()
</script>
4-10 import对应的模块化
ts 代码
// components.ts
export interface User {
name: string
}
export class Header {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Header'
document.body.appendChild(elem)
}
}
export class Content {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Content'
document.body.appendChild(elem)
}
}
export class Footer {
constructor() {
const elem = document.createElement('section')
elem.innerText = 'This is a Footer'
document.body.appendChild(elem)
}
}
// page.ts
import { Header, Content, Footer } from './components'
export default class Page {
constructor() {
new Header()
new Content()
new Footer()
}
}
使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js" integrity="sha512-vRqhAr2wsn+/cSsyz80psBbCcqzz2GTuhGk3bq3dAyytz4J/8XwFqMjiAGFBj+WM95lHBJ9cDf87T3P8yMrY7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="./dist/page.js"></script>
<script>
require(['page'], (page) => {
new page.default()
})
</script>
</body>
</html>
03-11.描述文件中的全局类型(上)
jquery.d.ts
// .d.ts(类型定义文件或类型描述文件) 的作用, 帮助我们理解 js 文件 或 js 库里面的内容的
// 全局变量
declare let $: (params: () => void) => void
// 全局函数
interface JqueryInstance {
html: (html: string) => JqueryInstance
}
// 函数重载
declare function $(readyFunc: () => void): void
// 一个函数可以有多种形式, 允许对同一个函数的名字进行多个全局函数声明
// declare function $(params: string): {
// html: (html: string) => {}
// }
declare function $(selector: string): JqueryInstance
// 使用interface的语法实现函数重载
interface Jquery {
(readyFunc: () => void): void
(selector: string): JqueryInstance
}
// 如何对对象进行类型定义,以及对类进行类型定义,以及命名空间的嵌套
declare namespace $ {
namespace fn {
class init {}
}
}
Page.ts
const teacher: string = 'tangshuo'
console.log(teacher)
$(function () {
$('body').html('<div>Hello</div>')
new $.fn.init()
})
03-13.模块代码的类型描述文件
// ES6 模块化
declare module 'jquery' {
interface JqueryInstance {
html: (html: string) => JqueryInstance
}
// 混合类型
function $(readyFunc: () => void): void
function $(selector: string): JqueryInstance
namespace $ {
namespace fn {
class init {}
}
}
}
03-14.泛型中keyof语法的使用
interface Person {
name: string
age: number
gender: string
}
class Teacher {
constructor(private info: Person) {}
// 类型保护 旧的方法
// getInfo(key: string) {
// if (key === 'name' || key === 'age' || key === 'gender') {
// return this.info[key]
// } else {
// throw new Error(`key '${key}' 不存在于 info 对象中`)
// }
// }
// type NAME = 'name'
// type T = 'name'
// key = 'name'
// Person[T]
// 类型保护 泛型方法
getInfo<T extends keyof Person>(key: T): Person[T] {
return this.info[key]
}
}
const teacher = new Teacher({ name: 'dell', age: 30, gender: 'male' })
const test = teacher.getInfo('name') // 代码提示 test 是 string 类型
const test2 = teacher.getInfo('age') // 代码提示 test2 是 number 类型
console.log(test)
05-01.类的装饰器1
// 类的装饰器
// 装饰器本身是一个函数
// 接受的参数是一个构造函数
// 装饰器通过 @ 符号来使用
// 装饰器运行时机: 类创建的时候运行,不是创建实例的时候运行
// function testDecorator(constructor: any) {
// constructor.prototype.getName = () => {
// console.log('DELL')
// }
// }
function testDecorator(flag: boolean) {
if (flag) {
return function (constructor: any) {
constructor.prototype.getName = () => {
console.log('DELL')
}
}
} else {
return function (constructor: any) {}
}
}
@testDecorator(true)
class Test {}
const test = new Test()
;(test as any).getName()
05-01.类的装饰器2
function testDecorator() {
return function <T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = 'lee'
}
}
}
// 函数执行后接受参数
const Test = testDecorator()(
class Test {
name: string
constructor(name: string) {
this.name = name
}
}
)
const test = new Test('dell')
console.log('test', test.getName())