TypeScript笔记 基础类型 unknown类型 unknown 类型用于解决any类型使用过于宽松的问题, unknown可以赋任何值,但是unknown不能赋值给其他参数(any和unknown除外) any类型甚至可以是function,但是unknown类型在赋值之前无法当做方法或者数组来使用
tuple元祖 TS的数据要求是同种类型的值组成,当需要不同类型的值组成组数时,就需要用到tuple 元祖需要在申明变量时就固定值类型与数量比如:
1 2 const tupleValue : [string , boolean , number ] = ['abc' , false , 123 ];
object,Object,{}的区别 object是一个数据类型,可用于声明变量,但是微软现在不建议这样使用,而是建议使用Record<string, unknown>
Object 是所有 Object 类的实例类型,与JS的Object使用类似 {} 是一个没有成员的对象,是一个具体的对象数据,不是一个类型 Never 类型 never 表示那些用不存在的值的类型,常用于断言或者保证方法逻辑性使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码
TypeScript 断言 可以在变量的前面加上,或者在变量后加上 as type,强调变量的类型. 如果想变量不为 undefined 或者 null,可以在赋值的时候后面加上!,例如:
1 2 3 4 function myFunc (maybeString: string | undefined | null ) { const onlyString : string = maybeString; const ignoreUndefinedAndNull : string = maybeString!; }
交叉类型 TS 中可以通过&符号将 2 个类型合并成一个新类型
1 2 3 4 5 6 7 type PartialPointX = { x : number ; };type Point = PartialPointX & { y : number ; };let point : Point = { x : 1 , y : 1 }
但是如果有同名变量但是类型(基础类型)不一致时,该变量类型会变成 never 如果是非基础类型,例如重新构建的对象类型,就可以同时接受多种类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface D { d : boolean ; }interface E { e : string ; }interface F { f : number ; }interface A { x : D; }interface B { x : E; }interface C { x : F; }type ABC = A & B & C;let abc : ABC = { x : { d : true , e : 'semlinker' , f : 666 } }; console .log ('abc:' , abc);
TypeScript 函数 与 JavaScript 的区别
ts 函数参数需要声明类型,函数的返回值类型也需要声明,可选参数也需要声明 1 2 3 4 function foo (name: string , gender: string = 'female, age?: number): void { }
有了严格的方法参数定义后,TS 就有了函数重载 1 2 3 4 5 6 7 8 9 10 11 function add (a: number , b: number ): number ;function add (a: string , b: string ): string ;function add (a: string , b: number ): string ;function add (a: number , b: string ): string ;function add (a: Combinable, b: Combinable ) { if (typeof a === 'string' || typeof b === 'string' ) { return a.toString () + b.toString (); } return a + b; }
TypeScript 接口 TS 比 JS 更加的面向对象,它是支持接口 interface 的.
1 2 3 4 5 6 7 8 9 interface Person { name : string ; age : number ; } let semlinker : Person = { name : "semlinker" , age : 33 , };
也可以在声明接口的时候设定变量为可读(readonly),同时也可以设置可选(!)变量
interface 与 type 的区别 两者都是用来描述对象的形状或者函数签名,与接口类型不一样,类型别名可以用于一些其他类型,比如原始类型、联合类型和元组,interface 主要用于描述对象和函数
1 2 3 4 5 6 7 8 9 10 11 12 type Name = string ;type PartialPointX = { x : number ; };type PartialPointY = { y : number ; };type PartialPoint = PartialPointX | PartialPointY ;type Data = [number , string ];
与类型别名不同,接口可以定义多次,会被自动合并为单个接口。
1 2 3 4 interface Point { x : number ; }interface Point { y : number ; }const point : Point = { x : 1 , y : 2 };
继承extends与实现Implements extends TS 对象的继承就类似于& 交叉合并,但是 extends 可读性更好
1 2 3 4 5 6 7 interface PartialPointX { x : number ; }interface Point extends PartialPointX { y : number ; }; type PartialPointX = { x : number ; };type Point = PartialPointX & { y : number ; };
不过一般情况下extends更加倾向于一个对象或者方法继承了父类,是面向对象的一种理念,而不是单纯的交叉合并.
Implements 实现 interface,但类不能实现使用类型别名定义的联合类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface Point { x : number ; y : number ; } class SomePoint implements Point { x = 1 ; y = 2 ; } type PartialPoint = { x : number ; } | { y : number ; };class SomePartialPoint implements PartialPoint { x = 1 ; y = 2 ; }
TypeScript 的面向对象 首先是支持静态变量和静态方法 static 静态就是所有的实例对象都公共的,例如:
1 2 3 4 5 class Person { static time : number ; name : string };
11.2 ECMAScript 私有字段 在 TypeScript 3.8 版本就开始支持ECMAScript 私有字段,使用方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person { #name : string ; constructor (name: string ) { this .#name = name; } greet ( ) { console .log (`Hello, my name is ${this .#name} !` ); } } let semlinker = new Person ("Semlinker" );semlinker.#name;
与常规属性(甚至使用 private 修饰符声明的属性)不同,私有字段要牢记以下规则:
私有字段以 # 字符开头,有时我们称之为私有名称;
每个私有字段名称都唯一地限定于其包含的类;
不能在私有字段上使用 TypeScript 可访问性修饰符(如 public 或 private);
私有字段不能在包含的类之外访问,甚至不能被检测到。
private 与# 的区别
私有变量可以使用 getter 和 setter 方法来访问或者修改私有变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let passcode = "Hello TypeScript" ;class Employee { private _fullName : string ; get fullName (): string { return this ._fullName ; } set fullName (newName: string ) { if (passcode && passcode == "Hello TypeScript" ) { this ._fullName = newName; } else { console .log ("Error: Unauthorized update of employee!" ); } } } let employee = new Employee ();employee.fullName = "Semlinker" ; if (employee.fullName ) { console .log (employee.fullName ); }
TypeScript 支持抽象 abstract 使用 abstract 关键字声明的类,我们称之为抽象类。抽象类不能被实例化,因为它里面包含一个或多个抽象方法。所谓的抽象方法,是指不包含具体实现的方法
1 2 3 4 5 6 7 8 abstract class Person { constructor (public name: string ){} abstract say (words : string ) :void ; } const lolo = new Person ();
抽象类不能被直接实例化,我们只能实例化实现了所有抽象方法的子类。具体如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 abstract class Person { constructor (public name: string ){} abstract say (words : string ) :void ; } class Developer extends Person { constructor (name: string ) { super (name); } say (words : string ): void { console .log (`${this .name} says ${words} ` ); } } const lolo = new Developer ("lolo" );lolo.say ("I love ts!" );
TypeScript 类方法支持重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class ProductService { getProducts (): void ; getProducts (id : number ): void ; getProducts (id?: number ) { if (typeof id === 'number' ) { console .log (`获取id为 ${id} 的产品信息` ); } else { console .log (`获取所有的产品信息` ); } } } const productService = new ProductService ();productService.getProducts (666 ); productService.getProducts ();
泛型语法 泛型顾名思义就是广泛的类型,不特指某种类型.语法上表现为.例如:
1 2 3 4 5 6 function identity <T, U>(value : T, message : U) : T { console .log (message); return value; } console .log (identity<Number , string >(68 , "Semlinker" ));
上面代码中的<T, U> 其实只是一个占位符,或者说是一个类型参数,当调用 identity 方法时,传入该位置的<Number, string>就取代了方法中 T 和 U 的位置,也就是说identity 方法就变成了:
1 2 3 4 function identity <Number , string >(value : Number , message : string ) : Number { console .log (message); return value; }
因为泛型只是个占位符,所以定义的时候用什么符号其实都可以,你也可以用<A><B><C>
, 但是使用上习惯用<T>
. 表示 Type 除了 T 之外,以下是常见泛型变量代表的意思:
K(Key):表示对象中的键类型;
V(Value):表示对象中的值类型;
E(Element):表示元素类型。
泛型可以使用在接口,类,方法.
装饰器 装饰器是什么? 首先看代码例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function Greeter (greeting: string ) { return function (target: Function ) { target.prototype .greet = function ( ): void { console .log (greeting); }; }; } @Greeter ("Hello TS!" )class Greeting { constructor ( ) { } } let myGreeting = new Greeting ();(myGreeting as any ).greet ();
需要注意的是,若要启用实验性的装饰器特性,你必须在命令行或 tsconfig.json 里启用 experimentalDecorators 编译器选项:
命令行:
1 tsc --target ES5 --experimentalDecorators
tsconfig.json
1 2 3 4 5 6 { "compilerOptions" : { "target" : "ES5" , "experimentalDecorators" : true } }
装饰器分类 装饰器大致上可以分为以下几种:
类装饰器
属性装饰器
方法装饰器
方法参数装饰器
类装饰器 对类使用,可传入一个隐式参数作为类的构造参数,或者不传 但是默认会将被装饰的类做为参数传入
1 2 3 4 5 6 7 8 9 10 11 12 function logClz (注意:参数装饰器只能用来监视一个方法的参数是否被传入;:any ) { params.prototype .url = 'xxxx' ; params.prototype .run = function ( ) { console .log ('run...' ); }; } @logClz class HttpClient { constructor ( ) { } } var http :any = new HttpClient ();http.run ();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function logClz (params:string ) { console .log ('params:' , params); return function (target:any ) { console .log ('target:' , target); target.prototype .url = params; } } @logClz ('hello' )class HttpClient { constructor ( ) { } } var http :any = new HttpClient ();console .log (http.url );
属性装饰器 对类的属性装饰的叫属性装饰器,有 2 个隐式参数:
对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象;其实就是当前类
属性成员名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function logProp (params:any ) { return function (target:any , attr:any ) { console .log (target) console .log (attr) target[attr] = params; target.api = 'xxxxx' ; target.run = function ( ) { console .log ('run...' ); } } } class HttpClient { @logProp ('http://baidu.com' ) public url :any |undefined ; constructor ( ) { } getData ( ) { console .log (this .url ); } } var http :any = new HttpClient ();http.getData (); console .log (http.api ); http.run ();
方法装饰器
方法装饰器被应用到方法的属性描述符上,可以用来监视、修改、替换方法的定义;
方法装饰器会在运行时传入3个参数:
对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象;(当前类)
成员的名字;(方法名)
成员的属性描述符;({value: ƒ, writable: true, enumerable: false, configurable: true} value就是方法体)
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 function get (params:any ) { console .log (params) return function (target:any , methodName:any , desc:any ) { console .log (target) console .log (methodName) console .log (desc) var oldMethod = desc.value ; desc.value = function (...args:any [] ) { let newArgs = args.map ((item )=> { return String (item); }); oldMethod.apply (this , newArgs); } } } class HttpClient { constructor ( ) { } @get ('http://baidu.com' ) getData (...args:any [] ) { console .log ('getData: ' , args); } } var http = new HttpClient ();http.getData (1 , 2 , true );
方法参数装饰器
参数装饰器表达式会在运行时被调用,可以为类的原型增加一些元素数据,传入3个参数:
对于静态成员来说是类的构造函数,对于实例成员来说是类的原型(类)
方法名称,如果装饰的是构造函数的参数,则值为undefined(functionName)
参数在函数参数列表中的索引;(参数下标)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function logParams (params:any ) { console .log (params) return function (target:any , methodName:any , paramIndex:any ) { console .log (target) console .log (methodName) console .log (paramIndex) } } class HttpClient { constructor ( ) { } getData (@logParams ('uuid' ) uuid:any ) { console .log (uuid); } } const http = new HttpClient ();http.getData ();
注意:参数装饰器只能用来监视一个方法的参数是否被传入;
装饰器的执行顺序
装饰器组合: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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 function logClz11 (params:string ) { return function (target:any ) { console .log ('logClz11' ) } } function logClz22 (params?:string ) { return function (target:any ) { console .log ('logClz22' ) } } function logAttr (params?:string ) { return function (target:any , attrName:any ) { console .log ('logAttr' ) } } function logMethod (params?:string ) { return function (target:any , methodName:any , desc:any ) { console .log ('logMethod' ) } } function logParam11 (params?:any ) { return function (target:any , methodName:any , paramIndex:any ) { console .log ('logParam11' ) } } function logParam22 (params?:any ) { return function (target:any , methodName:any , paramIndex:any ) { console .log ('logParam22' ) } } @logClz11 ('http://baidu.com' )@logClz22 ()class HttpClient { @logAttr () public url :string |undefined ; constructor ( ) { } @logMethod () getData ( ) { console .log ('get data' ); } setData (@logParam11 () param1:any , @logParam22 () param2:any ) { console .log ('set data' ); } }
装饰器参考链接