typescript学习笔记 
https://gitee.com/samuelsue/typescript-learning-notes 
 
配置ts运行环境 需要安装的依赖
1 2 3 4 5 6 7 8 9 10 11 12 "devDependencies" :  {     "@typescript-eslint/eslint-plugin" :  "^3.1.0" ,      "@typescript-eslint/parser" :  "^3.1.0" ,      "clean-webpack-plugin" :  "^3.0.0" ,      "eslint" :  "^7.2.0" ,      "html-webpack-plugin" :  "^4.3.0" ,      "ts-loader" :  "^7.0.5" ,      "webpack" :  "^4.43.0" ,      "webpack-cli" :  "^3.3.11" ,      "webpack-dev-server" :  "^3.11.0" ,      "webpack-merge" :  "^4.2.2"    }  
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 const  path = require ('path' )const  HtmlWebpackPlugin  = require ('html-webpack-plugin' );const  {CleanWebpackPlugin } = require ('clean-webpack-plugin' );module .exports  = {  entry : './src/index.ts' ,   output : {     filename : 'main.js' ,     path : path.resolve (__dirname,'../dist' )   },   resolve : {     extensions : ['.js' , '.ts' , '.tsx' ]   },   module : {     rules : [       {         oneOf :[           {             test : /\.tsx?$/ ,             use : 'ts-loader' ,             exclude : /node_modules/            }         ]       }     ]   },   devtool : process.env .NODE_ENV  === 'development' ?'eval-source-map' :'cheap-module-source-map' ,   plugins : [     new  HtmlWebpackPlugin ({       template : './src/template/index.html'      }),     new  CleanWebpackPlugin ()   ],   devServer : {     contentBase : '../dist' ,     compress : true ,     port : 8090 ,     open : true    } } 
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 module .exports  = {    "parser" : "@typescript-eslint/parser" ,     "parserOptions" : {         "project" :"./tsconfig.json" ,         "ecmaFeatures" : {             "jsx" : true          },         "ecmaVersion" : 2018 ,         "sourceType" : "module"      },     "extends" :[         "plugin:@typescript-eslint/recommended"      ],     "plugins" : [         "@typescript-eslint"      ],     "rules" : {     },          "ignorePatterns" : [       "webpack.config.js" ,       ".eslintrc.js"      ] }; 
package.json添加脚本命令
1 2 3 4 5 "scripts" :  {   "dev" :  "webpack-dev-server --mode=development --config ./build/webpack.config.js" ,    "build" :  "webpack --mode=production --config ./build/webpack.config.js" ,    "lint" :  "eslint src --ext .js,.ts --fix"  } , 
1. 基本类型 
boolean
number(支持二进制0b1101,八进制0o173,十六进制0x7b)
string
数组: 
number[], string[],...... 
Array<number'>.... 
联合类型 (string|number)[] 
 
元组类型
1 let  tuple :[string ,number ,boolean ] = ['a' ,1 ,false ] 长度和元素类型顺序都有要求
枚举类型
1 2 3 4 5 6 7 enum  Users  {  CREATOR ,    ADMIN ,     USER  = 3   } console .log (Users .CREATOR )console .log (Users [3 ])  
any类型
void类型 (undefined,在非strict模式下可以是null)
1 2 3 4 5 const  consoleText = (txt :string ):void  =>    console .log (txt)      } consoleText ('123' ) 
undefined
null
never类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  error (message : string never  {    throw  new  Error (message); } function  fail (    return  error ("Something failed" ); } function  infiniteLoop (never  {    while  (true ) {     } } const  neverVal = ():never  =>  throw  new  Error ('abc' ) } myStr = neverVal ()   
object(eslint不推荐使用)
1 2 3 4 5 6 function  printObj (obj :object void {  console .log (obj) } printObj ({  name : 'Samuel'  }) 
 
类型断言 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const  getLength = (target : (string  | number )): number  =>     if  ((<string >target).length  || (target as  string ).length  === 0 ) {     return  (<string >target).length    } else  {     return  target.toString ().length    } } getLength (123 )
类型断言有两种写法
(<类型>变量)(变量 as 类型) React只能这么写,因为<>会被当成标签  
2 Symbol Symbols是不可改变且唯一的。主要用法是用来做对象的属性名,这样可以保证属性名独一无二不被mixin覆盖
1 2 3 4 5 6 7 8 9 10 11 let  s1 = Symbol ();let  s2 = Symbol ();s1 === s2  let  s1 = Symbol ('foo' );let  s2 = Symbol ('foo' );s1 === s2  
类型转换 Symbol不可以和其他类型的值做运算,但是有toString方法,且可以转换成布尔
1 2 3 4 5 6 7 8 9 10 11 12 let  sym = Symbol ('My symbol' );"your symbol is "  + sym1  + symString (sym) sym.toString ()  Boolean (sym) !sym   
用处 Symbol可以用来解决强耦合的某一个具体的字符串或数字 
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 function  getArea (shape, options ) {  let  area = 0 ;   switch  (shape) {     case  'Triangle' :        area = .5  * options.width  * options.height ;       break ;        }   return  area; } const  shapeType = {  triangle : 'Triangle'  }; function  getArea (shape, options ) {  let  area = 0 ;   switch  (shape) {     case  shapeType.triangle :         area = .5  * options.width  * options.height ;       break ;   }   return  area; } const  shapeType = {  triangle : Symbol () }; 
Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。 
1 2 3 4 5 6 7 8 9 10 const  nameSymbol = Symbol ('name' )const  obj = {  [nameSymbol]: 'Samuel' ,   age :18  } for (const  key in  obj) {  console .log (key)   } console .log (Object .keys (obj))  console .log (Object .getOwnPropertyNames (obj)) 
但是可以被Object.getOwnPropertySymbols()和Reflect.ownKeys()获取
1 console .log (Object .getOwnPropertySymbols (obj)) 
Symbol.for(),Symbol.keyFor() 有时,我们希望重新使用同一个 Symbol 值,Symbol.for()方法可以做到这一点。
1 2 3 4 let  s1 = Symbol .for ('foo' );let  s2 = Symbol .for ('foo' );s1 === s2  
Symbol.keyFor()会返回一个使用Symbol.for(key)登记过的key
1 2 3 4 5 let  s1 = Symbol .for ("foo" );Symbol .keyFor (s1) let  s2 = Symbol ("foo" );Symbol .keyFor (s2) 
内置Symbol值 Symbol.hasInstance 对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)。
1 2 3 4 5 6 7 8 class  MyClass  {  [Symbol .hasInstance ](foo) {     console .log (foo)   } } [1 , 2 , 3 ] instanceof  new  MyClass () 
Symbol.isConcatSpreadable 对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。
1 2 3 4 5 6 7 let  arr1 = ['c' , 'd' ];['a' , 'b' ].concat (arr1, 'e' )  arr1[Symbol .isConcatSpreadable ]  let  arr2 = ['c' , 'd' ];arr2[Symbol .isConcatSpreadable ] = false ; ['a' , 'b' ].concat (arr2, 'e' )  
Symbol.species 对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。
1 2 3 4 5 6 7 8 9 class  MyArray  extends  Array  {} const  a = new  MyArray (1 , 2 , 3 );const  b = a.map (x  =>const  c = a.filter (x  =>1 );b instanceof  MyArray   c instanceof  MyArray   
子类MyArray继承了父类Array,a是MyArray的实例,b和c是a的衍生对象。你可能会认为,b和c都是调用数组方法生成的,所以应该是数组(Array的实例),但实际上它们也是MyArray的实例。
1 2 3 4 5 6 7 8 9 class  MyArray  extends  Array  {  static  get [Symbol .species ]() { return  Array ; } } const  a = new  MyArray ();const  b = a.map (x  =>b instanceof  MyArray   b instanceof  Array   
上面代码中,a.map(x => x)生成的衍生对象,就不是MyArray的实例,而直接就是Array的实例。
有关字符串的3个Symbol 
Symbol.match 
Symbol.replace 
Symbol.split 
 
1 2 3 String .prototype replace (searchValue, replaceValue)searchValue[Symbol .replace ](this , replaceValue) 
Symbol.iterator 对象的Symbol.iterator属性,指向该对象的默认遍历器方法。
1 2 3 4 5 6 7 8 9 const  myIterable = {};myIterable[Symbol .iterator ] = function * () {   yield  1 ;   yield  2 ;   yield  3 ; }; [...myIterable]  myIterable.next () 
Symbol.toPrimitive 当对象转换为基本类型时调用这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let  obj = {  [Symbol .toPrimitive ](hint) {     switch  (hint) {       case  'number' :         return  123 ;       case  'string' :         return  'str' ;       case  'default' :         return  'default' ;       default :         throw  new  Error ();      }    } }; 2  * obj 3  + obj obj == 'default'   String (obj) 
Symbol.toStringTag 对象的Symbol.toStringTag属性,指向一个方法。这个属性可以用来定制Object.prototype.toString.call(obj)的返回的[object Object]或[object Array]中object后面的那个字符串。
1 2 3 4 5 6 7 class  Collection  {  get [Symbol .toStringTag ]() {     return  'xxx' ;   } } let  x = new  Collection ();Object .prototype toString .call (x) 
3. 接口 限制对象属性 使用接口限制对象的属性和属性类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface  NameInterface  {  firstName : string ,   lastName : string  } function  getFullname ({firstName, lastName}: NameInterface  ): string  {  return  `${firstName} ·${lastName} `  } console .log (getFullname ({  firstName : "Samuel" ,   lastName : 'Sue'  })) 
可选属性 可选属性的限制
1 2 3 4 5 6 7 8 9 10 11 interface  VegetableInterface  {  color ?: string ,    type : string  } function  VegInfo ({color, type }: VegetableInterface  ): string  {  return  `${color || '' } ${type } `  } console .log (VegInfo ({type : 'Onion' }))
多余属性 对于多余属性,有三种方法解除属性限制
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 enum  Gender  {  Female ,   Male , } interface  PersonInfo  {  name : string ,   age : number ,   gender : Gender ,      [props : string ]: any  } function  logPerson ({name, age, gender}: PersonInfo  ): void  {  console .log (`Person:${name} ,${age}  years old,${gender} ` ) } logPerson ({  name : 'Samuel' ,   age : 25 ,   gender : Gender .Male ,   hobby :'Basketball'   }) logPerson (({  name : 'Samuel' ,   age : 25 ,   gender : Gender .Male ,   hobby :'Basketball'   }as  PersonInfo ))  const  person = {  name : 'Samuel' ,   age : 25 ,   gender : Gender .Male ,   hobby :'Basketball'   } logPerson (person)
注意,剩余属性(属性索引)是规定了其他属性的类型的,举个例子:
1 2 3 4 interface  Role {    [prop :string ]:number ,        } 
只读属性 1 2 3 4 5 6 7 8 interface  ArrInterface  {  0 : number ,    readonly  1 : string    } const  arr :ArrInterface  = [123 ,'hahah' ]
属性类型 1 2 3 4 5 6 7 8 9 10 interface  RoleDict  {  [id :string ]:number  } const  role :RoleDict  = {  12 :123 ,      } console .log (role['12' ])
接口继承 1 2 3 4 interface  Tomato  extends  VegetableInterface {  radius : number  } 
函数接口 函数接口: 注意函数类型写法(arg:string,arg2:number):void,即(参数:类型...):返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface  CounterInterface  {  (): void ,     counter : number  } const  countFn = ():CounterInterface  =>  const  fn = function  (     fn.counter ++   }   fn.counter  = 0    return  fn } const  countFunc :CounterInterface  = countFn ()countFunc ()console .log (countFunc.counter )countFunc ()console .log (countFunc.counter )
4. 函数 函数类型 1 2 3 4 5 6 7 interface  AddFnInterface  {  (x : number ,y : number ):number    } type  Add  = (x : number , y : number   number const  addFunc : Add  = (x : number , y : number   x + y
可选参数,默认参数 1 2 3 type  AddFnType  = (x : number , y ?: number   number const  fn2 : AddFnType  = (x = 3 , y ) =>  x + (y || 0 )
剩余参数 1 2 3 4 type  SumFnType  = (...args : number [] ) =>  number const  fn3 : SumFnType  = (...args ) =>  args.reduce ((previousValue, currentValue ) =>  previousValue + currentValue, 0 )console .log (fn3 (1 , 2 , 3 , 4 , 5 , 6 ))
函数重载 1 2 3 4 5 6 7 8 9 10 11 12 13 function  splitChar (x : number string []function  splitChar (x : string string []function  splitChar (x : any any  {    if  (typeof  x === 'string' ) {     return  x.split ('' )   } else  {     return  (x as  number ).toString ().split ('' )   } } console .log (splitChar (123456 ))console .log (splitChar ('我是爸爸' ))
5. 泛型 对于函数,传入的参数类型事前无法确定,在调用时确定,且返回值类型也需要根据参数类型确定,同时对于这种动态的写法我们有时仍需要对类型做判断以调用不同类型对象的方法。
这时候我们需要的就是泛型 
基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const  genArr = <T>(val : T, times : number ): T[] => {  return  new  Array (times).fill (val) } type  GenArr  = <T>(val : T, times : number   T[]const  genFn : GenArr  = (val, times ) =>  {  return  new  Array (times).fill (val) } genArr (123 ,3 ).map (item =>toFixed ())console .log (genArr ('爸爸' ,3 ).map (item =>split ('' )))  
泛型约束(结合接口) 1 2 3 4 5 6 7 8 9 10 interface  WithLength  {  length : number  } const  genFn2 = <T extends  WithLength >(val : T, times : number ): T[] => {  return  new  Array (times).fill (val) } genFn2 ([1 ,2 ,3 ],2 )genFn2 ('爸爸' ,2 )
理解泛型中的keyof 
http://semlinker.com/ts-keyof/ 
https://juejin.cn/post/6844903826558812167 
 
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 interface  WithLength  {    length : number  } const  genFn2 = <T extends  WithLength >(val : T, times : number ): T[] => {  return  new  Array (times).fill (val) } interface  Person {    name : string ;     age : number ;     gender : string ; } class  Teacher {    constructor (private  info :Person           getInfo<T extends  keyof Person >(key : T):Person [T] {         return  this .info [T];     } } const  teacher = new  Teacher ({  name : 'sssssss' ,   age : 18 ,   gender : 'male'  }) const  test = teacher.getInfo ('age' );console .log (test);
typeof语法的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const  COLORS  = {  red : 'red' ,   blue : 'blue'  } type  Colors  = typeof  COLORS  let  color : Colors ;color = 'red'  	 color = 'blue'  	 color = 'yellow'   
泛型约束(结合索引类型) 1 2 3 4 5 6 7 8 9 10 11 12 function  showProp<T, K extends  keyof T>(obj : T, prop : K) {  return  obj[prop] } const  person1 = {  name : 'Samuel' ,   age : 26 ,   gender : 'Male'  } console .log (showProp (person1,"age" ))
6. 类 es6的类 补充知识点: 
set/get关键字操作属性值 
私有化方法/属性(不是语法上的添加_,真正意义上的私有化) 
new.target 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  Parent {	constuctor (         if (new .target ===Parent ){             throw  new  Error ('父类不允许实例化' )         }     } } let  privateSymbol = new  Symbol ('name' )class  Child  extends  Parent {    constructor (name ){         super ()         this [privateSymbol] = name         console .log (new .target )     }     getName (         return  this [privateSymbol]     } } export  Child  new  Child ()  
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 class  Parent {    constructor (         this .name  = 'Parent'      }     showName (         return  this .name      }     static  getName (         return  Parent .name      } } class  Child  extends  Parent {    constructor (         super ()          this .name  = 'Child'      }     showParentName (         return  super .showName ()       }     static  getParentName (         return  super .getName ()       } } Child .getParentName () new  Child ().getParentName () 
TS中的类 属性修饰符 
public 
private(仅自身可用) 
protected(仅自身和派生类[子类]可用) 
 
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 class  Parent  {  protected  x : number       protected  constructor (x : number      this .x  = x   }   protected  getX (     return  this .x    } } class  Child  extends  Parent  {  constructor (x : number      super (x);     }   getX (): number  {          return  super .getX ();   } } const  ch = new  Child (234 )console .log (ch.getX ())
只读属性 1 2 3 4 5 6 7 8 9 10 11 12 class  ReadOnlyInfo  {  public  readonly  info : string    constructor (info : string      this .info  = info   } } const  rInfo = new  ReadOnlyInfo ('只能读,不能改' )console .log (rInfo)
参数修饰符 1 2 3 4 5 6 7 8 9 class  A  {  constructor (private  name : string         } } const  a = new  A ('Samuel' )console .log (a)  
静态成员属性 1 2 3 4 5 6 7 8 9 class  B  {  public  static  readonly  pi = 3.1415926    private  static  num = 2333  } console .log (B.pi )
可选属性/参数, 存取器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  Human  {  private  _name : string    public  age ?: number    constructor (name : string , age ?: number , public  gender ?: string      this ._name  = name     this .age  = age   }   public  set  name (newVal : string      this ._name  = newVal   }   public  get  name () {     return  this ._name .replace (/./ , substring  =>toUpperCase ())   } } const  h = new  Human ("samuel" )h.name  = 'zhangboy'  console .log (h.name )
抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 abstract  class  MyFile  {  protected  constructor (public  size : number    }   public  abstract  save (): void   } class  MediaFile  extends  MyFile  {  constructor (public  size : number , public  type : string      super (size)    }   save (): void  {     console .log ('save media file' )   }   play (): void  {     console .log ('play media File' )   } } 
类实现接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface  player {  supportType : string [],   play : () =>  void  } class  MusicPlayer  implements  player {  play (): void  {     console .log ('play music' )   }   supportType : string [];   constructor (...types : string [] ) {     this .supportType  = types   } } const  player = new  MusicPlayer ('mp3' , 'wav' , 'wma' , 'acc' )player.play () 
接口继承类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Point  {  protected  x : number ;    protected  y : number ;   protected  constructor (x : number , y : number      this .x  = x     this .y  = y   } } interface  Point3 d extends  Point  {  z : number ; } class  AcPoint  extends  Point  implements  Point3 d {  z : number ;   constructor (x : number , y : number , z : number      super (x, y)     this .z  = 312    } } 
类类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  TestE  {  public  info : string    constructor (     console .log ('constructor exec' )     this .info  = 'hhh'    } } const  testFn1 = <T>(c : new () => T): T  =>  return  new  c () } testFn1 (TestE )
7. 枚举 枚举值编号 1 2 3 4 5 6 7 8 9 10 const  f = 400 enum  Code  {  OK ,   Fault  = f,    Error  = 3 ,    Success  } console .log (Code [400 ]) 
字符串枚举 1 2 3 4 5 6 enum  reply {  Error  = 'Sorry, an error has occurred' ,   Success  = 'No problem' ,   Failed  = Error ,   } 
异构枚举(不推荐)1 2 3 4 5 enum  XX  {  a = 1 ,   b = 'hehe' , } 
枚举成员类型和联合枚举类型 如果一个枚举里所有成员的值都是字面量类型的值,那么这个枚举的每个成员和枚举本身都可以作为类型来使用。字面量枚举成员需满足以下条件:
不带初始值的枚举成员,例如 enum E { A } 
值为字符串字面量,例如 enum E { A = 'hello' } 
值为数字字面量,或者带有一元 - 符号的数字字面量,例如 enum E { A = 1 },enum E { A = -1 } 
 
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 enum  ShapeKind  {  Circle ,   Square  } interface  Circle  {  kind : ShapeKind .Circle     radius : number  } let  c : Circle  = {  kind : ShapeKind .Square ,    radius : 100  } enum  Status  {  on,   off } interface  Light  {  status : Status   } const  light :Light  = {  status : Status .off , } 
const枚举 普通的枚举在编译后会实实在在创建一个对象(枚举名同名),然后让变量指向对象的值(枚举值),使用const枚举后不会创建这个对象,而是直接用
1 2 3 4 5 6 7 8 const  enum  Animal {  Dog ,   Cat ,   GoldFish  } const  kitty = Animal .Cat   
8. 类型推断和类型兼容 类型推断 
ts会自动推断写的代码是什么类型 
当你不需要ts自动推断类型的时候 需要用到类型断言 
最佳通过类型推断 
上下文类型推断 
 
1 2 3 4 let  a = 1 ;let  b = [1 ,2 ]
类型兼容 
当一个类型Y可以被赋值给另外一个类型X时,我们就可以说类型X兼容类型Y 
结构之间兼容,成员少的兼容成员多的 
函数之间兼容,参数多的兼容参数少的 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let  fn1 = (x : string , y : string ): void  =>  console .log (11 ) } let  fn12 = (x : string ): void  =>  console .log (22 ) } fn1 = fn12 interface  Aitf  {  name :string  } interface  Bitf  {  name :string ,   age :number  } let  aObj : Aitf  = {name :'Samuel' }let  bObj : Bitf  = {name :'Samuel' ,age :25 }aObj = bObj 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function  shk (x : number , y : number number function  shk (x : string , y : string string function  shk (x : any , y : any any  {  return  x + y } function  shl (x : number , y : number number function  shl (x : any , y : any any  {  return  x + y } let  fna = shlfna = shk   
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 class  AC {     protected  num :number    private  age :number    constructor (public  name :string ,age :number      this .num  = 13      this .age  = age   } } class  ACsub  extends  AC {  constructor (public  name :string ,age :number      super (name,age);     this .num  = 15    } } class  BC {  constructor (public  name :string    } } let  ax :BC  = new  BC ('name' )  ax = new  AC ('hh' ,17 )    ax = new  ACsub ('hh' ,25 )   console .log (ax)
9. 高级类型 交叉类型 交叉类型是将多个类型合并为一个类型。 可以理解成逻辑与 
1 2 3 4 5 const  mergeObj = <T, U>(a : T, b : U): T & U  =>  let  res = {} as  T & U     res = Object .assign (a, b)   return  res } 
联合类型 可以理解成或 
1 2 3 4 const  showRes = (): string  | number  =>  const  arr = ['Samuel' , 123 ]   return  Math .random () > .5  ? arr[0 ] : arr[1 ] } 
注意一点,举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface  Bird  {  fly ():void    layEggs ():void  } interface  Turtle  {  swim ():void    layEggs ():void  } function  getSmallPet  (Bird |Turtle  {  return  {} as  Bird |Turtle  } getSmallPet ().layEggs () 
类型保护 如果我们想确定变量的类型,比如是不是string类型或者其他类型,以往我们需要做大量的类型断言,ts提供类型保护机制,可以方便使用
1 2 3 4 5 6 7 8 9 10 function  isString (x : string  | number string  {  return  typeof  x === 'string'  } const  res1 = showRes () if  (isString (res1)) {  console .log ('string length:' , res1.length ) } else  {   console .log ('number:' , res1.toFixed ()) } 
typeof做简单的类型保护 
typeof能判断的类型有:string/number/boolean/symbol/undefined/function,其他都是object
ts中用typeof做类型保护只能判断string/number/boolean/symbol,其他类型不会识别为类型保护
1 2 3 4 5 6 if  (typeof  res1 === 'string' ) {  console .log ('string length:' , res1.length ) } else  {   console .log ('number:' , res1.toFixed ()) } 
intanceof做类型保护 
判断是否是特定类创建的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Axx  {  public  age = 13  } class  Bxx  {  public  name = 'Sam'  } function  getRandomAB (  return  Math .random () > .5  ? new  Axx () : new  Bxx () } const  rsAB = getRandomAB ()if  (rsAB instanceof  Axx ) {  console .log (rsAB.age ) } else  {   console .log (rsAB.name ) } 
null/undefined类型断言 1 2 3 4 5 6 7 8 function  getPrefixStr (x :number |null   function  addPrefix (prefix :string string  {          return  prefix + x!.toFixed ().toString ()   }   x = x || 0.1    return  addPrefix ('pr' ) } 
类型别名 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type  Container <T> = { value : T };type  Tree <T> = {    value : T;     left : Tree <T>;     right : Tree <T>; } type  LinkedList <T> = T & { next : LinkedList <T> };interface  Person  {    name : string ; } var  people : LinkedList <Person >;var  s = people.name ;var  s = people.next .name ;var  s = people.next .next .name ;var  s = people.next .next .next .name ;
type可以给抽象类取别名,但是注意别名不能使用extends,implement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 abstract  class  AbsC {  protected  name :string    protected  constructor (     this .name  = 'Samuel'    } } type  absc = AbsC  class  CCC  extends  AbsC {  constructor (     super ();   } } 
字面量类型 1 2 3 4 5 6 7 8 9 type  Direction  = 'east' |'west' |'south' |'north' const  DirFn  = (direc :Direction   console .log (direc) } DirFn ('west' )
可辨识联合 
具有普通的单例类型属性   —可辨识的特征 。 
一个类型别名包含了那些类型的联合—联合 。 
此属性上的类型保护。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 interface  Square  {    kind : "square" ;     size : number ; } interface  Rectangle  {    kind : "rectangle" ;     width : number ;     height : number ; } interface  Circle  {    kind : "circle" ;     radius : number ; } type  Shape  = Square  | Rectangle  | Circle ; function  area (s : Shape          switch  (s.kind ) {         case  "square" : return  s.size  * s.size ;         case  "rectangle" : return  s.height  * s.width ;         case  "circle" : return  Math .PI  * s.radius  ** 2 ;     } } 
索引类型 keyof 
返回索引属性名构成的联合类型
1 2 3 4 5 6 interface  ACtp2  {  name : string ,   age : number  } let  k : keyof ACtp2   
索引访问操作符 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function  pluck<T, K extends  keyof T>(o : T, names : K[]): T[K][] {  return  names.map (n  => } interface  Person  {    name : string ;     age : number ; } let  person : Person  = {    name : 'Jarid' ,     age : 35  }; let  strings : string [] = pluck (person, ['name' ]); 
在普通的上下文里使用T[K]需要保证K extends keyof T
使用[]意味着取出属性(值或类型)
1 2 3 4 5 6 7 8 interface  Types  {  a : number ,   b : string ,   c : boolean , } type  AllTypes  = Types [keyof Types ]  
映射类型 简单例子:将一个类型的所有属性都变成可读
1 2 3 type  ReadOnlyTypes <T> = {  readonly  [P in  keyof T]: T[P]   } 
内置的映射类型:
Readonly  只读 
Partial  可选 
Pick<T,K>  T里面保留K(可以是联合类型)属性
Record<K extends keyof any, T>  创建一个类型,K代表键值的类型, T代表值的类型
1 2 3 4 5 6 7 8 9 10 function  record<K extends  string  | number , T, U>(obj : Record <K, T>, fn : (x : T  U): Record <K, U> {  const  res : any  = {}   for  (const  key in  obj) {     res[key] = fn (obj[key])   }   return  res } console .log (record (person2, (p ) =>  p.toString ().length ))
Omit<T,K> 实现排除已选的属性
1 2 3 4 5 6 type  User  = {  id : string ,   name : string ,   email : string  } type  UserWithoutEmail  = Omit <User , 'email' >;  
Exclude<T,U> 从T中提出可以赋值给U的类型
Extract<T, U> -- 提取T中可以赋值给U的类型。
NonNullable<T> -- 从T中剔除null和undefined。
ReturnType<T> -- 获取函数返回值类型。
InstanceType<T> -- 获取构造函数类型的实例类型。
 
可以显式的指定添加或着删除修饰符
1 2 3 4 5 6 type  RemoveReadonly <K> = {  -readonly  [P in  keyof K]: K[P]  } type  noReadOnlyTypes = RemoveReadonly <readTypess>
unknown类型 
any 和 unknown https://zhuanlan.zhihu.com/p/104296850 
 
任何类型都可以赋值给unknown类型
在没有类型断言或者基于控制流的类型细化时,unknown不可以赋值给其他类型
在没有类型断言或者基于控制流的类型细化时,不可以对unknown类型对象进行任何操作
unknown与其他类型组成交叉类型, 就等于其他类型
unknown与其他类型组成联合类型, 等于unknown
never是unknown的子类型
keyof unknown得到never
unknown类型只能进行===或!==操作, 不能进行其他操作
unknown类型不能访问属性,作为函数调用以及作为类创建对象
使用类型映射时如果映射的属性是unknown类型,就不会产生映射
1 2 3 4 5 6 type  NumsType <T> = {  [P in  keyof T]: number  } type  xxd = NumsType <any >  type  xxs = NumsType <unknown >  
 
条件类型 extends条件类型 
1 2 3 type  Typs <T> = T extends  string  ? string  : number let  vals2 : Typs <'2' >  let  vals3 : Typs <false >  
分布式条件类型 
1 2 3 type  onlyString<T> = (T extends  string  ? T : never )type  someTypes = 'a'  | 'b'  | 123  | false type  resultTypes = onlyString<someTypes>  
条件类型加索引类型 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 type  reserveFunction<T> = {  [P in  keyof T]: T[P] extends  () => void  ? P : never    }[keyof T]  interface  Part  {  user : 'Sam' ,   code : 132 ,   subparts : Part []   updateTime :()=> void ,   thankU (): void  } type  Res2  = reserveFunction<Part >const  obj2 : Res2  = 'updateTime' 
内置条件类型 
Exclude<T,U>  从T里面排除U
1 type  typesA = Exclude <'a' |'b' |'c' , 'a' |'b' > 
Extract<T,U>  从T中选取可以赋值给U的类型
1 type  typesB = Extract <'a' |'b' |'c' , 'a' |'b' > 
NonNullable<T> 去掉null类型(undefined,null)
1 type  typesC = NonNullable <'a'  | 'b'  | 'c'  | null  | undefined >  
ReturnType<T>  T的返回值类型
1 type  typesD = ReturnType <() =>  string >  
InstanceType<T>  ts中类有2种类型, 静态部分的类型和实例的类型, 所以T如果是构造函数类型, 那么InstanceType可以返回他的实例类型
1 2 3 4 5 6 7 8 9 10 11 12 class  AClass {  constructor (     console .log ('AClass' )   } } interface  AConstructor {  new (): string  } type  typeE = InstanceType <typeof  AClass >  type  typeF = InstanceType <AConstructor >  
Parameters<T> 获取函数参数类型
1 2 3 4 5 interface  A{    (a :number , b :string ):string []; } type  A1  = Parameters <A> 
ConstructorParameters 获取构造函数的参数类型
1 2 3 4 5 6 7 class  AClass {  constructor (public  name :string      console .log ('AClass' )   } } type  typeG = ConstructorParameters <typeof  AClass > 
 
推断元素类型 1 2 3 4 5 6 7 8 9 10 type  Type2 <T> = T extends  any [] ? T[number ] : T type  typesX = Type2 <string []>  type  typesX2 = Type2 <boolean []>  type  Type3 <T> = T extends  Array <infer U> ? U : Ttype  typesY = Type3 <string []>  type  typesY2 = Type3 <boolean []>  type  typesY3 = Type3 <[false ,123 ,'sam' ]>  
1 2 3 4 5 type  Parameters <T extends  (...args : any ) => any > = T extends  (...args : infer P) => any  ? P : never ;type  ConstructorParameters <T extends  new (...arg : any ) => any > = T extends  new (...arg : infer P) => any  ? P : never 
10. 命名空间 为了防止全局变量过多,使用命名空间来管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 namespace  Components {         export  class  Header  {         constructor (             const  elem = document .createElment ('div' );             elem.innerText  = 'this is header' ;             document .body .appendChild (elem);         }     }     export  class  Content {}     export  class  Footer {}          export  interface  User {         name :string      }          export  namespace  Sub {         export  class  Test {}     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 namespace  Home {    export  class  Page  {         constructor (             new  Components .Header ();             new  Components .Content ();             new  Components .Footer ();         }     } } 
这个不太好用,好像没有挺多应用。
在给第三方库的描述文件中有很多引用。 
11. 描述文件 针对全局的描述文件 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 declare  var  $ : (param :()=>void   void ;interface  JqueryInstance  {    html : (html :string   JqueryInstance  } declare  function  $ (readyFunc : ()=>void void declare  function  $ (selector :string JqueryInstance declare  namespace $ {     namespace  fn  {         class  init {}     } } 
针对模块的描述文件 
1 2 3 4 5 6 7 8 9 10 11 12 13 declare  module  'jquery'  {    interface  JqueryInstance  {     	html : (html :string   JqueryInstance  	}     declare  function  $ (readyFunc : ()=>void void  	declare  function  $ (selector :string JqueryInstance                export  = $; } 
12. 装饰器 装饰器本身就是一个函数
类装饰器 类装饰器接收的参数是构造函数 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  testDecorator  (constructor :new  (...args:any [])=>any   constructor.prototype getName  = () =>  {     console .log ('dell' )   }   console .log ('decorator' ) } function  testDecorator1  (constructor :any   console .log ('decorator1' ) } @testDecorator1 @testDecorator class  Test  {}(new  Test () as  any ).getName () 
装饰器如果要支持接收参数,那么可以用闭包做装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function  decor1  (flag :boolean   if  (flag) {     return  function  (constructor :any                 constructor.prototype info  = () =>  {         console .log ('dell' )       }     }   }   return  function  (constructor :any  } @decor1 (true )  class  Test1  {}
如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function  testDecor<T extends  new  (...args :any [])=>any >(constructor :T) {  return  class  extends  constructor {     name = 'lee' ;     getName  () {       console .log (this .name )     }   } } @testDecor class  Person  {  name : string ;   constructor  (name :string      this .name  = name   } } (new  Person ('sam' ) as  any ).getName ()  
装饰器工厂函数写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  decoratorFactory  (  return  function  <T extends  new  (...args :any [])=>any >(constructor :T) {     return  class  extends  constructor {       name = 'lee' ;       getName  () {         console .log (this .name )       }     }   } } const  Person1  = decoratorFactory ()(class  {  name : string ;   constructor  (name :string      this .name  = name   } }) new  Person1 ('sam' ).getName () 
方法装饰器 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 function  funcDecor  (target : any , key :string , descriptor :PropertyDescriptor      console .log (target)   console .log (key)    console .log (descriptor)          const  oldVal = descriptor.value  as  (...args :any [])=> any    descriptor.value  = function  (...args :any [] ) {     return  oldVal.apply (this , args) + 'NO!!!!!!!!!!!'    } } class  UObj  {  name :string    constructor  (name :string      this .name  = name   }      @funcDecor    getName  () {     return  this .name    } } const  objU = new  UObj ('sam' )console .log (objU.getName ())
访问器装饰器 注意: set和get不能同时绑定,目前用处不知道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function  visitDecor  (target :any , key :string , descriptor :PropertyDescriptor   console .log ('descriptor:' , descriptor) } class  XObj  {  private  _name :string ;   constructor  (name :string      this ._name  = name   }   @visitDecor    get name  () {     return  this ._name    }      set name  (name :string ) {     this ._name  = name   } } 
访问器装饰器和方法装饰器区别:
访问器装饰器的descriptor有set,get, 没有value和writable 
方法装饰器没有set/get,有value和writable 
 
属性装饰器 属性装饰器不接受descriptor参数,但是可以返回一个新的descriptor来覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function  propDecorator  (target :any , key :string any  {           target[key] = 'NoNoNO'  } class  XaObj  {  @propDecorator    name='Dell'  } const  xao = new  XaObj ()xao.name  = 'Samuel'  console .log (xao.name ) console .log ((xao as  any ).__proto__ .name ) 
参数装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  paramDecorator  (target : any , method : string , paramIndex :number   console .log (target, method, paramIndex) } class  Sth  {  getInfo  (name :string , @paramDecorator  age : number ) {     console .log (name, age)   } } new  Sth ().getInfo ('Sam' , 30 )
执行顺序 元数据可以理解为在任意对象上挂载了一个Map,用于绑定键值信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  'reflect-metadata' function  Role  (name :string ClassDecorator  {     return  target  =>     Reflect .defineMetadata ('role' , name, target.prototype      console .log (target)   } } @Role ('admin' )class  Post  {}const  metaData = Reflect .getMetadata ('role' , Post .prototype console .log (metaData)const  np = new  Post ()const  anotherMd = Reflect .getMetadata ('role' , (np as  any ).__proto__ )console .log (anotherMd)
13. 问题 import type https://juejin.cn/post/6844904034952806414