TypeScript 基础再次学习
TypeScript 基础知识 1, 安装使用 全局安装
初始化文件夹配置文件
一般使用,直接输入tsc
,就自动把 ts 编译成 js
为了方便,可以监听变化,保存就自动编译
2. 基础类型 TypeScript 提供了 JavaScript 中所有的基础数据类型,如:
boolean
number
string
array
tuple
enum
any
void
null and undefined
never
ts 允许我们显示标注类型,或者不标也行,可以隐式推断
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 let isDone : boolean = false ;let decimal : number = 6 ;let color : string = 'blue' ;let list : number [] = [1 , 2 , 3 ];let list : Array <number > = [1 , 2 , 3 ]; let x : [string , number ]; x = ['hello' , 10 ]; enum Color { Red , Green , Blue , } let c : Color = Color .Green ;let notSure : any = 4 ;notSure = 'maybe a string instead' ; notSure = false ; function warnUser ( ): void { console .log ('This is my warning message' ); } let unusable : void = undefined ;let u : undefined = undefined ;let n : null = null ;
3. 联合类型 TypeScript 的一大特性是能够明确指定一个值可能的类型范围。
1 2 3 4 let value : number | string ;value = 'hello' ; value = 100 ; value = true ;
4. 类型断言 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。主要是因为 ts 的类型检查只能在编译时进行,编译后会抹去类型,变成普通的 js 代码
1 2 3 let someValue : any = 'this is a string' ;let strLength : number = (<string >someValue).length ; let strLength : number = (someValue as string ).length ;
5. 元组和枚举 元组类型允许表现一个已知元素数量和类型的数组,各元素的类型不必相同。
1 2 3 4 let tupleType : [string , number ];tupleType = ['hello' , 10 ]; console .log (tupleType[0 ].substring (1 )); console .log (tupleType[1 ].substring (1 ));
枚举是一种特殊的类型,它可以包含一组命名的常量。枚举可以是数字枚举,字符串枚举,异构枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 enum Color { Red , Green , Blue , } let c : Color = Color .Green ;enum Direction { Up = 'UP' , Down = 'DOWN' , Left = 'LEFT' , Right = 'RIGHT' , }
6. 接口 接口在 TypeScript 中是一个重要的概念,它是对行为的抽象,也是鸭子类型的一种表现。
鸭子类型 - 维基百科
1 2 3 4 5 6 7 8 9 10 interface LabelledValue { label : string ; } function printLabel (labelledObj: LabelledValue ) { console .log (labelledObj.label ); } let myObj = { size : 10 , label : 'Size 10 Object' };printLabel (myObj);
(简单来说就是,如果你定义了一个接口的结构,然后造了个对象有这个接口一模一样的属性,就算是造这个对象的时候没直接用 implement,TypeScript 也会认为这个对象是这个接口的实现,可以传入某个接受这个接口类型的函数当参数,不会报错)
7. 泛型 泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持。
1 2 3 4 5 function identity<T>(arg : T): T { return arg; } let output = identity<string >('myString' );
8. 装饰器与反射元数据 8.1 装饰器 装饰器,顾名思义,可以添加一些附加功能在代码中。在 TypeScript 中,装饰器提供了一种方法,能在运行时修改类的行为或对类进行额外的处理。它们可以应用在类,方法,属性或者参数等多个地方。这是一个很强大的功能,允许对类的行为进行扩展,而无需改变类的定义。
装饰器有四种类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function sealed (constructor: Function ) { Object .seal (constructor); Object .seal (constructor.prototype ); } @sealed class Greeter { greeting : string ; constructor (message: string ) { this .greeting = message; } greet ( ) { return 'Hello, ' + this .greeting ; } }
在这个例子中,@sealed
装饰器会在运行时应用到Greeter
类上,导致无法向Greeter
类添加新的属性,并且已有的属性无法删除或配置。
8.2 反射元数据 反射元数据是一种在设计阶段添加和读取元数据的方式。反射 API 包含很多功能,如查询类,接口,参数和返回类型等的设计类型。这为库作者提供了一种检查类结构的强大工具。
在 TypeScript 中使用反射元数据,需要使用reflect-metadata
库,并在 tsconfig.json 文件中开启emitDecoratorMetadata
选项。
这是一个基础的反射元数据使用例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import 'reflect-metadata' ;class MyClass { @validate public someMethod (someArg: any ) { } } function validate (target: Object , propertyKey: string | symbol , descriptor: TypedPropertyDescriptor<any > ) { let originalMethod = descriptor.value ; descriptor.value = function (...args: any [] ) { let metadataValue = Reflect .getMetadata ('design:paramtypes' , target, propertyKey); if (metadataValue !== someArg) { throw new Error (`Validation failed on argument ${someArg} ` ); } return originalMethod.apply (this , args); }; return descriptor; }
在这个例子中,validate
装饰器使用反射 API 来获取someMethod
方法参数的设计类型,并检查是否和方法被调用时传入的参数类型一致。如果不一致,就抛出一个错误。
练习 1. 封装 fetch,实现简单 axios 功能 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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' ;interface RequestConfig { url : string ; method?: Method ; body?: Object ; headers?: Record <string , string >; } interface AxiosResponse <T = any > { data : T; status : number ; statusText : string ; headers : Record <string , string >; config : RequestConfig ; request : {}; } class MyAxios { requestInterceptors : Array <(config: RequestConfig ) => RequestConfig > = []; responseInterceptors : Array <(response: AxiosResponse<any > ) => AxiosResponse <any >> = []; useRequestInterceptor (interceptor : (config: RequestConfig ) => RequestConfig ): void { this .requestInterceptors .push (interceptor); } useResponseInterceptor (interceptor : (response: AxiosResponse<any > ) => AxiosResponse <any >): void { this .responseInterceptors .push (interceptor); } private async request<T = any >(config : RequestConfig , method : Method ): Promise <AxiosResponse <T>> { this .requestInterceptors .forEach ((interceptor ) => { config = interceptor (config); }); const res : Response = await fetch (config.url , { method, body : config.body ? JSON .stringify (config.body ) : undefined , headers : { 'Content-Type' : 'application/json' , ...config.headers , }, }); if (!res.ok ) { throw new Error (`${res.status} ${res.statusText} ` ); } const data : T = await res.json (); const headers : Record <string , string > = {}; res.headers .forEach ((value: string , key: string ) => { headers[key] = value; }); let axiosResponse : AxiosResponse <T> = { data, status : res.status , statusText : res.statusText , headers, config, request : {}, }; this .responseInterceptors .forEach ((interceptor ) => { axiosResponse = interceptor (axiosResponse); }); return axiosResponse; } async get<T = any >(url : string ): Promise <AxiosResponse <T>>; async get<T = any >(config : RequestConfig ): Promise <AxiosResponse <T>>; async get<T = any >(url_or_config : string | RequestConfig ): Promise <AxiosResponse <T>> { let config : RequestConfig ; if (typeof url_or_config === 'string' ) { config = { url : url_or_config }; } else { config = url_or_config; } return await this .request <T>(config, 'GET' ); } async post<T = any >(config : RequestConfig ): Promise <AxiosResponse <T>> { return await this .request <T>(config, 'POST' ); } async put<T = any >(config : RequestConfig ): Promise <AxiosResponse <T>> { return await this .request <T>(config, 'PUT' ); } async delete <T = any >(config : RequestConfig ): Promise <AxiosResponse <T>> { return await this .request <T>(config, 'DELETE' ); } } const myAxios : MyAxios = new MyAxios ();myAxios.useRequestInterceptor ((config: RequestConfig ) => { config.headers = { ...config.headers , 'X-My-Custom-Header' : 'CustomHeaderValue' , }; return config; }); myAxios.useResponseInterceptor ((response: AxiosResponse<any > ) => { response.data = { ...response.data , fromInterceptor : '来自响应拦截器,这是ツユ的歌曲列表:' , }; return response; }); myAxios.get ('http://162.14.111.196:4000/artist/songs?id=34505358' ).then ((res: AxiosResponse ) => { console .log ('测试get\n' ); console .log (res); console .log (res.data .fromInterceptor + '\n' ); res.data .songs .forEach ((song: any ) => { console .log (song.al .name ); if (song.tns ) { console .log (song.tns [0 ]); } console .log (); }); });
2. 类型体操之 DeepReadonly 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type X = { x : { a : 1 ; b : 'hi' ; c : { d : true ; e : 'false' ; f : 3 ; }; }; y : 'hey' ; }; type DeepReadonly <T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly <T[P]> : T[P]; }; type Todo = DeepReadonly <X>;