- typescript由微軟開發的一款開源編程語言。
- ts是jacascript的超集,遵循ES6,ES5規范,ts擴展了js的語法。
- ts更像后端java,c#這樣的面向對象的語言,可以讓js開發大型的企業項目,
- 谷歌也大力支持ts的推廣,谷歌的angular2.x就是基於ts語法開發的
- 最新的Vue,React也繼承ts
- Nodejs框架Nestjs,midway中用的就是ts語法.
基礎配置
安裝:npm i -g typescript
驗證是否安裝成功:tsc -v
Version 4.4.3
編譯ts:tsc index.ts(需要編譯的ts文件)
ts開發工具 vscode自動編譯ts
- 創建tsconfig.json文件
tsc --init 生成配置文件
只改tsconfig:outdir:'./js' - 自動生成js
vscode-->終端-->運行任務-->typescript-->tsc:監視
一、ts中的類型
ts中為了是編寫的代碼更加規范,更有利於維護,增加了類型校驗。
類型賦值錯誤會報錯,
主要類型有:
類型 | 名稱 | 解釋 | 寫法 |
---|---|---|---|
布爾類型 | boolean | true/false | var flag:boolean=true |
數值類型 | number | 整數/小數 | var num:number=123 |
字符串類型 | string | 字符串 | var str:string='123' |
數組類型 | array | 數組 | let arr:number[]=[1,2,3](只能放number類型)/let arr:Array
|
元組類型 | tuple | 數組的一種可以指定數組里每項類型 | let arr:[string,number,boolean]=['ww',123,true] |
枚舉類型 | enum | enum 枚舉名稱{標識符[=整形常數]...} | enum Flag{success:1,err:-1},var f:Flag=Flag.err enum Color={red,blue,orage} ,var c:Color=Color.blue //c==1 |
任意類型 | any | 任意的值 | let num:any='123' |
null、undefined | 其他類型子類型 | var num:number | |
void類型 | void | 沒有任何類型,一般用於函數沒有返回值 | function run():void {} |
其他類型 | never | 包括null,undefined子類型,代表從不會出現的值 | never的變量只能被never類型賦值 |
二、函數定義
ES5函數定義
函數聲明
function run() {
return 'run'
}
匿名函數
var run2 = function () { }
ts函數定義方法
1-函數聲明
function run1(): string {
return 'aa'
}
2-匿名函數
var fun2 = function (): number {
return 123
}
3-傳參
function getInfo(name: string, age: number): string {
return `${name}++${age}`
}
console.log(getInfo('asd', 22))
4-無返回值
```function run3(): void { }````
ts方法的可選參數
ES5方法的實參和形參可以不一樣,ts中必須一樣,如果不一樣需要配置可選參數
注意:可選參數必須配置到參數最后面,用?表示可選
function getInfos(name: string, age?: number): string {
return `${name}++${age}`
}
默認參數
es5里面沒法設置默認參數,es6和ts找那個可以設置默認參數
默認參數可以不傳,類似於可選參數
function getInfo1(name: string, age: number = 20): string {
return `${name}++${age}`
}
剩余參數
1-改進前寫法
function sum(a: number, b: number, c: number, d: number): number {
return a + b + c + d
}
2-改進后
三點運算符 接收新傳過來的值
function sum1(...arr: number[]): number {
var sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
alert(sum1(1, 2, 3, 4))
另一種
function sum2(a: number, ...arr: number[]): number {
var sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return a + sum
}
alert(sum2(1, 2, 3, 4))
函數重載
java中方法的重載,:兩個或以上同名函數,但他們參數不一樣,這是會出現函數重載情況
ts中的從在,通過為一個函數提供多個函數類型定義來實現多種功能的目的
ts為了箭筒es5和es6從在寫法和java中有區別
es5中出現同名方法,下面的會替換上面的方法
ts重載
function getcon(name: string): string;
function getcon(age: number): number;
function getcon(name: string,age: number): string;
function getcon(str: any,age?:number): any {
if (typeof str == 'string') {
return '姓名' + str
} else {
return '年齡' + str
}
}
三、對象的繼承
es5繼承
-
es5里面的構造函數
function Person() { this.name = '張珊' this.age = 20 this.run = function () { } }
-
原型鏈上的屬性會被多個實例共享,構造函數不會
function Person() { this.name = '張珊' this.age = 20 this.run = function () { } } Person.prototype.sex = '男' Person.prototype.work = function () { console.log(this.name) } Person.getInfo = function () { // 靜態方法 } var p = new Person() p.work() // 調用靜態方法 Person.getInfo()
-
構造函數與原型鏈增加方法
function Person() { this.name = '張珊' this.age = 20 this.run = function () { } } // 原型鏈上的屬性會被多個實例共享,構造函數不會 Person.prototype.sex = '男' Person.prototype.work = function () { console.log(this.name) } Person.getInfo = function () { // 靜態方法 } var p = new Person() p.work()
-
類里面的靜態方法
function Person() { this.name = '張珊' this.age = 20 } // 定義靜態方法 Person.getInfo = function () { } var p = new Person() // 調用靜態方法 Person.getInfo()
-
es5繼承
原型鏈+對象冒充function Person(age) { this.name = '張珊' this.age = age } Person.prototype.sex = '男' Person.prototype.work = function () { console.log(this.name) } var p = new Person(18) // 對象冒充繼承---不可以繼承原型鏈的屬性和方法 function Web(){ Person.call(this) } // 原型鏈繼承-----實例化后沒法給父類傳參 Web.prototype=new Person() var son=new Web()
ts繼承
1、ts定義類
class Person {
name: string; //屬性 省略了public關鍵詞
constructor(n: string) {
this.name = n
}//構造函數,實例化類的時候觸發的方法
run(): void { }
}
var p = new Person('aa')
p.run()
2、ts中實現繼承
關鍵字:extends,super
子類與父類有相同方法,子類優先級更高
class Person1 {
name: string; //屬性 省略了public關鍵詞
constructor(n: string) {
this.name = n
}//構造函數,實例化類的時候觸發的方法
run(): string {
return `${this.name}是`
}
}
class Webs extends Person1 {
constructor(name: string) {
super(name)//類似於初始化父類構造函數
}
work() { }
}
var w = new Webs('李四')
3、ts類里面的修飾符
ts里面定義屬性的時候給我們提供了三種修飾符
-
public :公有:在類里面,子類,類外面都可以訪問
-
protected :保護類型:在類里面,子類都可以訪問,類外面不可以訪問
-
private:私有:在類里面可以訪問,子類,類外面都不可以訪問
-
屬性不加修飾符默認公有public
class Person2 {
name: string; //屬性 省略了public關鍵詞
private age: number
constructor(n: string, a: number) {
this.name = n
this.age = a
}//構造函數,實例化類的時候觸發的方法
run(): string {
return `${this.name}是`
}
}
4、靜態屬性 靜態方法
es5里面的
function Person(){}
Person.run2=function(){}//靜態方法
ts里面的
靜態方法無法調用類里面的屬性,可以調用靜態屬性
class Person3 {
static sex = 'nam'//靜態屬性
public name: string
constructor(name: string) {
this.name = name
}
run() { }
static prient() {console.log(this.sex,Person3.sex) }//靜態方法
}
var p = new Person3('11')
Person3.prient()//靜態方法調用
5、多態
父類定義一個方法不去實現,讓繼承它的子類去實現,每個子類有不同的表現
多態屬於繼承的一種表現
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+'吃魚'
}
}
6、抽象方法
- ts中的抽象類,他提供其他類集成的基類,不能直接被實例化
- 用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現並且必須在派生類中實現
- abstract抽象方法只能放在抽象類里面
- 抽象類的子類必須事項抽象類里面的方法
抽象類和抽象方法用來定義標准
標准:Animal這個類要求他的子類必須包含eat
abstract class Animals{
/**
* name
*/
public name:string
constructor(name:string){
this.name=name
}
abstract eat():any;
}
// 抽象類的子類必須事項抽象類里面的方法
class Dogs extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name+'吃肉'
}
}
四、接口
接口的作用:在面向對象的編程中,接口是一種規范的定義,它定義了行為和動作的規范,在程序設計里面,換口起到一種限制和規范的作用。接口定義了某一批央所雷要遵守的現范,接口不關心這些類的內部狀態數據,也不關心這些類里方法的實現細節,它只規定這批類里必須提供某些方法,提供這些方法的類就可以滿足實際需要。typescrip中的接口類似於java,同時還增加了更靈活的接口類型,包括屬性面數、可索引和類
1.屬性接口
1、對json的約束
ts自定義方法傳入對json進行約束
function prientLabel(labelInfo: { label: string }): void {
}
prientLabel({ name: 'aa' })//錯誤寫法
prientLabel({ label: 'aa' })//正確寫法
2、對批量方法進行約束
接口:行為和動作規范,對批量方法進行約束
參數順序可以不一樣,但必須得有
關鍵詞:interface
interface FullName {
firstName: string;//注意;結束
secondName: string;
}
function prientName(name: FullName) {
// 必須傳入對象 firstName secoendName
console.log(name.firstName, name.secondName)
}
var obj = { firstName: '1', secondName: 'jj', age: 1 }
prientName(obj)
prientName({ firstName: '1', secondName: 'jj', age: 1 })//傳age有問題
接口:可選屬性
interface FullNames {
firstName: string;//注意;結束
secondName?: string;
}
案例
interface Config {
type: string;
url: string;
data?: string
dataType: string
}
function ajax(config: Config) {
var xhr = new XMLHttpRequest()
xhr.open(config.type, config.url, true)
xhr.send(config.data)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
if (config.dataType == 'json') {
JSON.parse(xhr.response)
}
}
}
}
ajax({
type: 'get',
url: 'http://..',
data: '',
dataType: 'json'
})
2.函數類型接口
對傳入的參數以及返回值進行約束
加密函數類型接口
interface encrypt {
(ke: string, value: string): string
}
var md5: encrypt = function (key: string, value: string): string {
return key + value
}
3.可索引接口
數組,對象的約束(不常用)
interface UserArr {
[index: number]: string
}
var arr: UserArr = ['11', '22']
interface UserObj {
[index: string]: string
}
var objw: UserObj = { name: '20' }
4.類類型接口
對類進行約束和抽象類有點相似
關鍵字:implements
interface Animal1 {
name: string;
eat(str: string): void
}
class Dog implements Animal1 {
name: string;
constructor(name: string) {
this.name = name
}
eat() {
}
}
5.接口擴展
接口可以繼承接口
interface Animalss {
eat(): void
}
interface Person extends Animalss {
work(): void
}
class Son implements Person {
name: string
constructor(name: string) {
this.name = name
}
eat() { }
work() { }
}
class Progeammer {
name: string
constructor(name: string) {
this.name = name
}
coding() { }
}
class web extends Progeammer implements Person {
constructor(name: string) {
super(name)
}
eat() { }
work() { }
coding() { }
}
五、泛型
- 泛型,軟件工程中,我們不僅要創建一致的定義良好的API,同時也要考慮可重用性。 組件不僅能夠支持當前的數據類型,同時也能支持未來的數據類型,這在創建大型系統時為你提供了十分靈活的功能。
- 在像C#和Java這樣的語言中,可以使用泛型來創建可重用的組件,一個組件可以支持多種類型的數據。這樣用戶就可以以自己的數據類型來使用組件。
- 通俗理解:泛型就是解決類 接日 方法的復用性、以及對不特定數據類型的支持
普通寫法
只能返回string類型的數據
function getData(value:string):string{return value}
返回多種類型的數據 any可以解決這種問題(any放棄了類型檢查)
function getData1(value:any):any{return value}
1.泛型定義
- 傳入和返回類型相同
- 可以支持不特定的數據類型
- T表示泛型,具體什么類型是調用這個方法的時候決定的
function getData2<T>(value:T):T{return value} getData2<number>(123) // getData2<number>(‘kkk’)/* 錯誤寫法 */
2.泛型類
普通寫法
class Minclass{
public list:number[]=[]
add(num:number){
this.list.push(num)
}
min():number{
var min=this.list[0]
for (let i = 0; i < this.list.length; i++) {
if(min>this.list[i]){
min=this.list[i]
}
}
return min
}
}
泛型類寫法
class Minclass1<T>{
public list:T[]=[]
add(num:T):void{
this.list.push(num)
}
min():T{
var min=this.list[0]
for (let i = 0; i < this.list.length; i++) {
if(min>this.list[i]){
min=this.list[i]
}
}
return min
}
}
var m1=new Minclass1<number>()
3.泛型接口
interface ConfigFn{
(value:string,value2:string):string
}
var setData:ConfigFn=function(val:string,val2:string):string{
return val+val2
}
interface ConfigFn1{
<T>(value:T,value2:T):T
}
var setData1:ConfigFn1=function<T>(val:T,val2:T):T{
return val
}
setData1<string>('123','11')
interface ConfigFn2<T>{
(value:T):T
}
function getData6<T>(val:T):T{
return val
}
var myGetData:ConfigFn2<string>=getData6
myGetData('ll')
案例
class User{
name:string|undefined;
pass:string|undefined
}
class Mysql{
add(user:User):boolean{
return true
}
}
var u=new User()
u.name='zhang'
u.pass='123'
var db=new Mysql()
db.add(u)
泛型封裝
class User1{
name:string|undefined;
pass:string|undefined
}
class Mysql1<T>{
add(info:T):boolean{
return true
}
}
var u=new User1()
u.name='zhang'
u.pass='123'
var db1=new Mysql1<User1>()
db1.add(u)
案例
功能:定義一個操作數據庫的庫支持 Mysql MssqlMongoDb
要求1:Mysql MsSqlMongoDb功能一樣都有 add update delete get方法
注意:約束統一的規范、以及代碼重用
解決方案:需要約束規范所以要定義接口,需要代碼重用所以用到泛型
1、接口:在面向對象的編程中,接口是一種規范的定義,它定義了行為和動作的規范
2、泛型 通俗理解:泛型就是解決類 接口 方法的復用性、
interface DBI<T>{
add(info:T):boolean;
update(info:T,id:number):boolean
delete(id:number):boolean
get(id:number):any[]
}
// 定義一個操作mysql數據庫的類
class MysqlDb<T> implements DBI<T>{
add(info: any): boolean {
throw new Error("Method not implemented.")
}
update(info: any, id: number): boolean {
throw new Error("Method not implemented.")
}
delete(id: number): boolean {
throw new Error("Method not implemented.")
}
get(id: number): any[] {
throw new Error("Method not implemented.")
}
}
// 操作用戶表 定義一個User類和數據庫映射表
class Users{
name:string|undefined
pass:string|undefined
}
var u=new Users()
u.name='00'
u.pass='ii'
var omysql=new MysqlDb<Users>()
omysql.add(u)
六、模塊
- 模塊的的概念(官方):
- 關於木語的一點說明:請務必注意一點,TypeScript 1.5里木語名已經發生了變化。“內部模塊”現在稱做“命名空間”。外部模塊“現在則簡稱為“模塊#模塊在且自身的作用域里執行,而不是在全局作用域里:
- 這意味着定義在一個模塊里的變量,因數,類等等在模塊外部是不可見的,除非你明確地使用export形式之一導出它們。相反,如果想使用其它模塊導出的變量,國數,類,接口等的時候,你必須要導入它們,可以使用 import形式之一。
- 模塊的概念(自己理解):
- 我們可以把一些公共的功能單獨抽離成一個文件作為一個模塊。
- 模塊里面的變量 函數 類等默認是私有的,如果我們要在外部訪問模塊里面的數據(變量、函數、類),我們要要通過export暴露模塊里面的數據(變量、團數、類。。》。
- 暴露后我們通過 import 引入模塊就可以使用模塊里面暴露的數據(變量、函數、類...)。
自定義模塊
var dburl='xxx'
export function getData():any[]{
return[{
title:'123'
},{
title:'456'
}]
}
導入
import {getData} from './db'
getData()
export default 默認導出
每個模塊都可以有一個default導出,默認導出使用default關鍵字標記;並且一個模塊只能夠有一個default導出,需要使用一個特殊的導入形式
七、命名空間
- 在代碼量較大的情況下,為了避免各種變量命名相沖突,可將相似功能的函數、類、接口等放置到命名空間內
- 同Java的包、.Net的命名空間一樣,TypeScript的命名空間可以將代碼包裹起來,只對外暴露需要在外部訪問的對象。命名空間內的對象通過export關鍵字對外暴露。
命名空間和模塊的區別
- 命名空間:內部模塊,主要用於組織代碼,避免命名沖突。
- 模 塊:ts的外部模塊的簡稱,側重代碼的復用,一個模塊里可能會有多個命名空間。
namespace A{
interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃狗糧。`);
}
}
export class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃貓糧。`);
}
}
}
namespace B{
interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃狗糧。`);
}
}
export class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃貓糧。`);
}
}
}
var c=new B.Cat('小花');
c.eat();
封裝:模塊化+命名空間
export namespace A{
interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 在吃狗糧。`);
}
}
export class Cat implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃貓糧。`);
}
}
}
import {A} from './modules/animal';
var d=new A.Dog('小黑');
d.eat();
八、裝飾器
屬性裝飾器
裝飾器工廠
裝飾器定義
- 裝飾器:裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明,方法,屬性或參數上,可以修改類的行為。
- 通俗的講裝飾器就是一個方法,可以注入到類、方法、屬性參數上來擴展類、屬性、方法、參數的功能。
- 常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、參數裝飾器
- 裝飾器的寫法:普通裝飾器(無法傳參) 、 裝飾器工廠(可傳參)
- 裝飾器是過去幾年中js最大的成就之一,已是Es7的標准特性之一
類裝飾器
類裝飾器:類裝飾器在類聲明之前被聲明(緊靠着類聲明)。 類裝飾器應用於類構造函數,可以用來監視,修改或替換類定義。 傳入一個參數
類裝飾器:普通裝飾器(無法傳參)
function logClass(params:any){
console.log(params);
// params 就是當前類
params.prototype.apiUrl='動態擴展的屬性';
params.prototype.run=function(){
console.log('我是一個run方法');
}
}
@logClass
class HttpClient{
constructor(){
}
getData(){
}
}
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();
類裝飾器:裝飾器工廠(可傳參)
function logClass(params:string){
return function(target:any){
console.log(target);
console.log(params);
target.prototype.apiUrl=params;
}
}
@logClass('http://www.itying.com/api')
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(){
this.apiUrl=this.apiUrl+'----';
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個參數:
- 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
- 成員的名字。
類裝飾器
function logClass(params:string){
return function(target:any){
// console.log(target);
// console.log(params);
}
}
屬性裝飾器
function logProperty(params:any){
return function(target:any,attr:any){
console.log(target);
console.log(attr);
target[attr]=params;
}
}
@logClass('xxxx')
class HttpClient{
@logProperty('http://itying.com')
public url:any |undefined;
constructor(){
}
getData(){
console.log(this.url);
}
}
var http=new HttpClient();
http.getData();
方法裝飾器
它會被應用到方法的 屬性描述符上,可以用來監視,修改或者替換方法定義。
方法裝飾會在運行時傳入下列3個參數:
2. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
2. 成員的名字。
3. 成員的屬性描述符。
方法裝飾器一
function get(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc);
target.apiUrl='xxxx';
target.run=function(){
console.log('run');
}
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
@get('http://www.itying,com')
getData(){
console.log(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);
console.log(methodName);
console.log(desc.value);
//修改裝飾器的方法 把裝飾器方法里面傳入的所有參數改為string類型
//1、保存當前的方法
var oMethod=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(){
}
@get('http://www.itying,com')
getData(...args:any[]){
console.log(args);
console.log('我是getData里面的方法');
}
}
var http=new HttpClient();
http.getData(123,'xxx');
方法參數裝飾器
參數裝飾器表達式會在運行時當作函數被調用,可以使用參數裝飾器為類的原型增加一些元素數據 ,傳入下列3個參數:
- 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
- 方法的名字。
- 參數在函數參數列表中的索引。
function logParams(params:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log(params);
console.log(target);
console.log(methodName);
console.log(paramsIndex);
target.apiUrl=params;
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
getData(@logParams('xxxxx') uuid:any){
console.log(uuid);
}
}
var http:any = new HttpClient();
http.getData(123456);
console.log( http.apiUrl);
裝飾器執行順序
屬性》方法》方法參數》類
如果有多個同樣的裝飾器,它會先執行后面的
function logClass1(params:string){
return function(target:any){
console.log('類裝飾器1')
}
}
function logClass2(params:string){
return function(target:any){
console.log('類裝飾器2')
}
}
function logAttribute1(params?:string){
return function(target:any,attrName:any){
console.log('屬性裝飾器1')
}
}
function logAttribute2(params?:string){
return function(target:any,attrName:any){
console.log('屬性裝飾器2')
}
}
function logMethod1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法裝飾器1')
}
}
function logMethod2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法裝飾器2')
}
}
function logParams1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法參數裝飾器1')
}
}
function logParams2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法參數裝飾器2')
}
}
@logClass1('http://www.itying.com/api')
@logClass2('xxxx')
class HttpClient{
@logAttribute1()
@logAttribute2()
public apiUrl:string | undefined;
constructor(){
}
@logMethod1()
@logMethod2()
getData(){
return true;
}
setData(@logParams1() attr1:any,@logParams2() attr2:any,){
}
}
var http:any=new HttpClient();