TypeScript

TypeScript文档


数据类型

字符串

  let a:string = '123'
  let str:string = `ddd${a}`

数字类型

  let notNumber:number = NaN // NaN
  let num:number = 123 // 普通数字
  let infinityNumber:number = Infinity // 无穷大
  let decimal:number = 6 // 10进制
  let hex:number = 0xf00d // 十六进制 
  let binary:number = 0b1010 // 二进制
  let octal:number = 0o744 // 八进制s 

布尔类型

  let createBoolean = new Boolean()
  let booleand:boolean = true
  let booleand2:boolean = Boolean(1)

空值类型

  function voidFn():void {
    console.log('test')
  }
  // void也可以定义undefined和null
  let u:void = undefined
  let n:void = null

Null和Undefined类型

  let u:undefined = undefined
  let n:null = null

注:void和undefined和null最大区别
与void的区别是,undefined和null是所有类型的子类型。也就是说undefined类型的变量可以复制给string类型的变量

Any类型和Unknown顶级类型

nodejs 环境执行typescript
npm i @types/node --save-dev (node环境支持的依赖必装)
npm i typescript-node --g

  // any类型随时可切换复制
  let anys:any = 123
  anys = '123'
  anys = true
  // 声明变量没有指定任何类型默认为any
  let anys
  anys = '123'
  anys = true
  
  // unknown 可以定义任何类型的值
  let value: unknown;
  
  value = true;             // OK
  value = 42;               // OK
  value = "Hello World";    // OK
  value = [];               // OK
  value = {};               // OK
  value = null;             // OK
  value = undefined;        // OK
  value = Symbol("type");   // OK
  
  // 这样写会报错unknow类型不能作为子类型只能 作为父类型 any可以作为父类型和子类型
  //unknown类型不能赋值给其他类型
  let names:unknown = '123'
  let names2:string = names
  
  // 这样就没问题 any类型是可以的
  let names:any = '123'
  let names2:string = names   
  
  // unknown可赋值对象只有unknown 和 any
  let bbb:unknown = '123'
  let aaa:any= '456'

  // 如果是any类型在对象没有这个属性的时候还在  获取是不会报错的
  let obj:any = {b:1}
  obj.a
  
  
  // 如果是unknow 是不能调用属性和方法
  let obj:unknown = {
    b:1,
    ccc:():number=>213}
    obj.b
    obj.ccc()

接口和对象类型

  • 在typescript中,我们定义对象要用关键字interface(接口),我的理解是使用interface来定义一种约束,让数据的结构满足约束的格式,定义方式如下:
  // 这样写是会报错的 因为我们在person定义了a,b但是对象里面缺少b属性
  // 使用接口约束的时候不能多一个属性也不能少一个属性
  // 必须与接口保持一致
  interface Person {
    b:string,
    a:string
  }
 
  const person:Person  = {
    a:"213"
  }

  //重名interface  可以合并
  interface A{name:string}
  interface A{age:number}
  var x:A={name:'xx',age:20}
  //继承
  interface A{
    name:string
  }
  
  interface B extends A{
    age:number
  }
  
  let obj:B = {
    age:18,
    name:"string"
  }
  • 可选属性 使用?操作符
  //可选属性的含义是该属性可以不存在
  //所以说这样写也是没问题的
  interface Person {
    b?:string,
    a:string
  }
 
  const person:Person  = {
    a:"213"
  }
  • 任意属性 [propName:string]
    需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都是它的类型的子集
  // 在这个例子当中我们看到接口中并没有定义C但是并没有报错
  // 应为我们定义了[propName: string]: any;
  // 允许添加新的任意属性
  interface Person {
    b?:string,
    a:string,
    [propName: string]: any;
  }
 
  const person:Person  = {
    a:"213",
    c:"123"
  }
  • 只读属性 readonly
    readonly只读属性是不允许被赋值的只能读取
  //这样写是会报错的
  //应为a是只读的不允许重新赋值
  interface Person {
    b?: string,
    readonly a: string,
    [propName: string]: any;
  }
 
  const person: Person = {
    a: "213",
    c: "123"
  }
 
  person.a = 123
  • 添加函数
  interface Person {
    b?: string,
    readonly a: string,
    [propName: string]: any;
    cb():void
  }
 
  const person: Person = {
    a: "213",
    c: "123",
    cb:()=>{
        console.log(123)
    }
  }

数组类型

  • 数组的类型
    类型[]
  // 类型加中括号
  let arr:number[] = [123]
  // 这样会报错定义了数字类型出现字符串是不允许的
  let arr:number[] = [1,2,3,'1']
  // 操作方法添加也是不允许的
  let arr:number[] = [1,2,3,]
  arr.unshift('1')
 
 
  let arr: number[] = [1, 2, 3]; // 数字类型的数组
  let arr2: string[] = ["1", "2"]; // 字符串类型的数组
  let arr3: any[] = [1, "2", true]; // 任意类型的数组
  • 数组泛型
  let arr:Array<number> = [1,2,3,4,5]
  • 用接口表示数组
  interface NumberArray {
    [index:number]:number
  }
  let fibonacci:NumberArray = [1,1,2,3,5]
  
  // 表示:只要索引的类型是数字时,那么值的类型必须是数字。
  • 多维数组
  let data:number[][] = [[1,2], [3,4]];
  • argumentypescript数组
  function Arr(...args:any): void {
    console.log(argumentypescript)
    // 错误的argumentypescript 是类数组不能这样定义
    let arr:number[] = argumentypescript
  }
  Arr(111, 222, 333)
 
 
 
  function Arr(...args:any): void {
    console.log(argumentypescript) 
    // typescript内置对象IArgumentypescript 定义
    let arr:IArgumentypescript = argumentypescript
  }
  (111, 222, 333)
 
  // 其中 IArgumentypescript 是 TypeScript 中定义好了的类型,它实际上就是:
  interface IArgumentypescript {
    [index: number]: any;
    length: number;
    callee: Function;
  }
  • any在数组中的应用
  let list: any[] = ['test', 1, [],{a:1}]

元祖类型

如果需要一个固定大小的不同类型值的集合,我们需要使用元组

  let arr:[number,string] = [1,'string']

  let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]

枚举类型

  • 数字枚举
  enum Types{
    Red,
    Green,
    Blue
  }
  // 默认就是0开始,可以不写值
  • 字符串枚举
  enum Types{
    Red = 'red',
    Green = 'green',
    Blue = 'blue'
  }
  • 易构枚举
  enum Types{
    No = "no",
    yes = 1
  }
  • 接口枚举
  enum Types {
    yyds,
    dddd
  }
  interface A {
    red:Types.yyds
  }
 
  let obj:A = {
    red:Types.yyds
  }
  • const枚举
  const enum Types{
    No = "No",
    Yes = 1,
  }
  • 反向映射

它包含了正向映射( name -> value)和反向映射( value -> name)

  enum Enum{
    fall
  }
  let a = Enum.fall
  console.log(a); // 0
  let nameOfA = Enum[a]
  console.log(nameOfA); // fall

Never类型

TypeScript 将使用 never 类型来表示不应该存在的状态

  // 返回never的函数必须存在无法达到的终点
 
  // 因为必定抛出异常,所以 error 将不会有返回值
  function error(message: string): never {
    throw new Error(message);
  }
 
  // 因为存在死循环,所以 loop 将不会有返回值
  function loop(): never {
    while (true) {
    }
  }
  • never与void的差异
    // void类型只是没有返回值 但本身不会出错
    function Void():void {
      console.log();
    }
 
    // 只会抛出异常没有返回值
    function Never():never {
      throw new Error('aaa')
    }
  • never类型的一个应用场景
  interface A {
    type: "foo"
  }
 
  interface B {
    type: "bar"
  }

  interface C {
    type: "bizz"
  }
  type All = A | B | C;
  function handleValue(val: All) {
    switch (val.type) {
      case 'foo':
        break;
      case 'bar':
        break
      default:
        // 兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了    
        const exhaustiveCheck:never = val;
        break
    }
  }

Symbol类型

symbol类型的值是通过Symbol构造函数创建的。
可以传参作为唯一标识,只支持string和number类型的参数

  let sym1 = Symbol();
  let sym2 = Symbol("key"); // 可选的字符串key
  • Symbol的值是唯一的
  const s1 = Symbol()
  const s2 = Symbol()
  // s1 === s2 =>false
  • 用作对象属性的键
  let sym = Symbol();
  let obj = {
    [sym]: "value"
  };
  console.log(obj[sym]); // "value"
  • 使用symbol定义的属性,是不能通过如下方式遍历拿到的
  const symbol1 = Symbol('666')
  const symbol2 = Symbol('777')
  const obj1= {
   [symbol1]: '小满',
   [symbol2]: '二蛋',
   age: 19,
   sex: '女'
  }
  // 1 for in 遍历
  for (const key in obj1) {
   // 注意在console看key,是不是没有遍历到symbol1
   console.log(key)
  }
  // 2 Object.keys 遍历
  Object.keys(obj1)
  console.log(Object.keys(obj1))
  // 3 getOwnPropertyNames
  console.log(Object.getOwnPropertyNames(obj1))
  // 4 JSON.stringfy
  console.log(JSON.stringify(obj1))

如何拿到

  // 1 拿到具体的symbol 属性,对象中有几个就会拿到几个
  Object.getOwnPropertySymbols(obj1)
  console.log(Object.getOwnPropertySymbols(obj1))
  // 2 es6 的 Reflect 拿到对象的所有属性
  Reflect.ownKeys(obj1)
  console.log(Reflect.ownKeys(obj1))
  • Symbol.iterator 迭代器 和 生成器 for of

支持遍历大部分类型迭代器 arr nodeList argumetns set map 等

  var arr = [1,2,3,4];
  let iterator = arr[Symbol.iterator]();
  console.log(iterator.next());  //{ value: 1, done: false }
  console.log(iterator.next());  //{ value: 2, done: false }
  console.log(iterator.next());  //{ value: 3, done: false }
  console.log(iterator.next());  //{ value: 4, done: false }
  console.log(iterator.next());  //{ value: undefined, done: true }

测试用例

  interface Item {
    age: number,
    name: string
  }
  const array: Array<Item> = [{ age: 123, name: "1" }, { age: 123, name: "2" }, { age: 123, name: "3" }]
 
  type mapTypes = string | number
  const map:Map<mapTypes,mapTypes> = new Map()
 
  map.set('1','王爷')
  map.set('2','陆北')
 
  const obj = {
    aaa:123,
    bbb:456
  }
 
  let set:Set<number> = new Set([1,2,3,4,5,6])
  // let it:Iterator<Item> = array[Symbol.iterator]()
  const gen = (erg:any): void => {
    let it: Iterator<any> = erg[Symbol.iterator]()
    let next:any= { done: false }
    while (!next.done) {
        next =  it.next()
        if (!next.done) {
            console.log(next.value)
        }
    }
  }
  gen(array)

以下为这些symbols的列表:

  • Symbol.hasInstance

方法,会被instanceof运算符调用。构造器对象用来识别一个对象是否是其实例。

  • Symbol.isConcatSpreadable

布尔值,表示当在一个对象上调用Array.prototype.concat时,这个对象的数组元素是否可展开。

  • Symbol.iterator

方法,被for-of语句调用。返回对象的默认迭代器。

  • Symbol.match

方法,被String.prototype.match调用。正则表达式用来匹配字符串。

  • Symbol.replace

方法,被String.prototype.replace调用。正则表达式用来替换字符串中匹配的子串。

  • Symbol.search

方法,被String.prototype.search调用。正则表达式返回被匹配部分在字符串中的索引。

  • Symbol.species

函数值,为一个构造函数。用来创建派生对象。

  • Symbol.split

方法,被String.prototype.split调用。正则表达式来用分割字符串。

  • Symbol.toPrimitive

方法,被ToPrimitive抽象操作调用。把对象转换为相应的原始值。

  • Symbol.toStringTag

方法,被内置方法Object.prototype.toString调用。返回创建对象时默认的字符串描述。

  • Symbol.unscopables

对象,它自己拥有的属性会被with作用域排除在外。

泛型

  • 函数泛型
  function Add<T>(a: T, b: T): Array<T>  {
    return [a,b]
  }
  Add<number>(1,2)
  Add<string>('1','2')
  
  function Sub<T,U>(a:T,b:U):Array<T|U> {
    const params:Array<T|U> = [a,b]
    return params
  }
  Sub<Boolean,number>(false,1)
  • 定义泛型接口
  interface MyInter<T> {
   (arg: T): T
  }
 
  function fn<T>(arg: T): T {
   return arg
  }
 
  let result: MyInter<number> = fn
 
  result(123)
  • 对象字面量泛型
  let foo: { <T>(arg: T): T }
 
  foo = function <T>(arg:T):T {
    return arg
  }
 
  foo(123)
  • 泛型约束
  function getLegnth<T>(arg:T) {
    return arg.length
  }

  interface Len {
    length:number
  }
 
  function getLegnth<T extends Len>(arg:T) {
    return arg.length
  }
 
  getLegnth<string>('123')
  • 使用keyof约束对象
  function prop<T, K extends keyof T>(obj: T, key: K) {
    return obj[key]
  }
  
  let o = { a: 1, b: 2, c: 3 }
 
  prop(o, 'a') 
  prop(o, 'd') // 此时就会报错发现找不到
  • 泛型类
  class Sub<T>{
    attr: T[] = [];
    add (a:T):T[] {
      return [a]
    }
  }
 
  let s = new Sub<number>()
  s.attr = [1,2,3]
  s.add(123)
 
  let str = new Sub<string>()
  str.attr = ['1','2','3']
  str.add('123')

函数扩展

  • 函数的类型
  // 注意,参数不能多传,也不能少传 必须按照约定的类型来
  const fn = (name: string, age:number):string => {
    return name + age
  }
  fn('张三',18)
  • 函数的可选参数
  // 通过?表示该参数为可选参数
  const fn = (name: string, age?:number): string => {
    return name + age
  }
  fn('张三')
  • 函数参数的默认值
  const fn = (name: string = "我是默认值"): string => {
    return name
  }
  fn()
  • 接口定义函数
  // 定义参数 num 和 num2  :后面定义返回值的类型
  interface Add {
    (num:  number, num2: number): number
  }
 
  const fn: Add = (num: number, num2: number): number => {
    return num + num2
  }
  fn(5, 5)
 
 
  interface User{
    name: string;
    age: number;
  }
  function getUserInfo(user: User): User {
    return user
  }
  • 定义剩余参数
  // 定义参数 num 和 num2  :后面定义返回值的类型
  interface Add {
    (num:  number, num2: number): number
  }
 
  const fn: Add = (num: number, num2: number): number => {
    return num + num2
  }
  fn(5, 5)
 
 
  interface User{
    name: string;
    age: number;
  }
  function getUserInfo(user: User): User {
    return user
  }
  • 函数重载
    重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。

    如果参数类型不同,则参数类型应设置为 any。

    参数数量不同你可以将不同的参数设置为可选。

  function fn(params: number): void
 
  function fn(params: string, params2: number): void
 
  function fn(params: any, params2?: any): void {
 
    console.log(params)
 
    console.log(params2)
 
  }
 
  fn(123)
 
  fn('123',456)

类型断言|联合类型|交叉类型

  • 联合类型
  // 例如我们的手机号通常是13XXXXXXX 为数字类型 这时候产品说需要支持座机
  // 所以我们就可以使用联合类型支持座机字符串
  let myPhone: number | string  = '010-820'
 
 
  // 这样写是会报错的应为我们的联合类型只有数字和字符串并没有布尔值
  let myPhone: number | string  = true

函数使用联合类型

  const fn = (something:number | boolean):boolean => {
    return !!something
  }
  • 交叉类型
    多种类型的集合,联合对象将具有所联合对象的所有成员
  interface People {
    age: number,
    height: number
  }
  interface Man{
    sex: string
  }
  const xiaoman = (man: People & Man) => {
    console.log(man.age)
    console.log(man.height)
    console.log(man.sex)
  }
  xiaoman({age: 18,height: 180,sex: 'male'});
  • 类型断言
  interface A {
    run: string
  }
 
  interface B {
    build: string
  }
 
  const fn = (type: A | B): string => {
    return (type as A).run
  }
  // 可以使用类型断言来推断他传入的是A接口的值 

需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:

  • 使用any临时断言
  window.abc = 123
  // 这样写会报错因为window没有abc这个东西
  (window as any).abc = 123
  // 可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的。

内置对象

  • ECMAScript的内置对象
    Boolean、Number、String、RegExp、Date、Error
  let b: Boolean = new Boolean(1)
  console.log(b)
  let n: Number = new Number(true)
  console.log(n)
  let s: String = new String('哔哩哔哩关注小满zs')
  console.log(s)
  let d: Date = new Date()
  console.log(d)
  let r: RegExp = /^1/
  console.log(r)
  let e: Error = new Error("error!")
  console.log(e)
  • DOM和BOM的内置对象
    Document、HTMLElement、Event、NodeList等
  let body: HTMLElement = document.body;
  let allDiv: NodeList = document.querySelectorAll('div');
  // 读取div 这种需要类型断言 或者加个判断应为读不到返回null
  let div:HTMLElement = document.querySelector('div') as HTMLDivElement
  document.addEventListener('click', function (e: MouseEvent) {
    
  });
  // dom元素的映射表
  interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
  }
  • 定义Promise
  function promise():Promise<number>{
  return new Promise<number>((resolve,reject)=>{
       resolve(1)
   })
  }
 
  promise().then(res=>{
    console.log(res)
  })

Class类

  class Person {
    name:string
    age:number = 0
    constructor (name:string,age:number){
      this.name = name
      this.age = age
    }
    run () {

    }
  }
  • 类的修饰符 public private protected
    使用public修饰符可以让你定义的变量,内部可访问也可以外部访问,如果不写就是默认public
    使用private修饰符代表定义的变量私有的,只能在内部访问,不能再外部访问
    使用protected修饰符,代表定义的变量私有的只能在内部和继承子类中访问,不能在外部访问
  class Person {
    public name:string
    private age:number 
    protected some:any
    constructor (name:string,ages:number,some:any) {
       this.name = name
       this.age = ages
       this.some = some
    }
    run () {
 
    }
  }
 
  class Man extends Person{
    constructor () {
        super("张三",18,1)
        console.log(this.some)
    }
    create () {
       console.log(this.some)
    }
  }
  let xiaoman = new Person('小满',18,1)
  let man = new Man()
  man.some
  • static 静态属性和静态方法
    我们用static定义的属性,不可以通过this去访问,只能通过类型去调用
    static静态函数,同样也是不能通过this去调用,也是通过类名去调用
    需注意:如果两个函数都是static静态的是可以通过this互相调用的
  • interface定义类
    typescript interface 定义类 使用关键字 implementypescript 后面跟interface的名字多个用逗号隔开 继承还是用extends
   
  interface PersonClass {
    get(type: boolean): boolean
  }
 
  interface PersonClass2{
    set():void,
    asd:string
  }
 
  class A {
    name: string
    constructor() {
        this.name = "123"
    }
  }
 
  class Person extends A implementypescript PersonClass,PersonClass2 {
    asd: string
    constructor() {
        super()
        this.asd = '123'
    }
    get(type:boolean) {
        return type
    }
    set () {
 
    }
  }
  • 抽象类

应用场景如果你写的类实例化之后毫无用处此时我们可以把它定义为抽象类
或者你也可以把他作为一个基类->通过继承一个派生类去实现基类的一些方法

我们在A类定义了 getName 抽象方法但为实现

我们B类实现了A定义的抽象方法 如不实现就不报错 我们定义的抽象方法必须在派生类实现

  abstract class A {
   name: string
   constructor(name: string) {
      this.name = name;
   }
   print(): string {
      return this.name
   }
 
   abstract getName(): string
  }
 
  class B extends A {
   constructor() {
      super('小满')
   }
   getName(): string {
      return this.name
   }
  }
 
  let b = new B();
 
  console.log(b.getName());

类型推断|类型别名

  • 类型推断

声明了一个变量但是没有定义类型
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论

  • 类型别名

type 关键字(可以给一个类型定义一个名字)多用于符合类型
定义类型名

  type str = string
 
  let s:str = "我是小满"
 
  console.log(s);

定义函数别名

  type str = () => string
 
  let s: str = () => "我是小满"
 
  console.log(s);

定义联合类型别名

  type str = string | number
 
  let s: str = 123
 
  let s2: str = '123'
 
  console.log(s,s2);

定义值的别名

  type value = boolean | 0 | '213'

  let s:value = true
  // 变量s的值  只能是上面value定义的值

tsconfig.ts配置文件

详细配置

"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}
 
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
   "src/**/*"
],
// 指定一个排除列表(include的反向操作)
 "exclude": [
   "demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
 "files": [
   "demo.ts"
]

常用的:

  • include

指定编译文件默认是编译当前目录下所有的ts文件

  • exclude

指定排除的文件

  • target

指定编译js 的版本例如es5 es6

  • allowJS

是否允许编译js文件

  • removeComments

是否在编译过程中删除文件中的注释

  • rootDir

编译文件的目录

  • outDir

输出的目录

  • sourceMap

代码源文件

  • strict

严格模式

  • module

默认common.js 可选es6模式 amd umd 等

NameSpace命名空间

  namespace a {
    export const Time: number = 1000
    export const fn = <T>(arg: T): T => {
      return arg
    }
    fn(Time)
  }
 
  namespace b {
    export const Time: number = 1000
    export const fn = <T>(arg: T): T => {
      return arg
    }
    fn(Time)
  }

  a.Time
  b.Time
  • 嵌套命名空间
  namespace a {
    export namespace b {
      export class Vue {
        parameters: string
        constructor(parameters: string) {
          this.parameters = parameters
        }
      }
    }
  }
 
  let v = a.b.Vue
 
  new v('1')
  • 抽离命名空间

a.ts

  export namespace V {
    export const a = 1
  }

b.ts

  import {V} from '../observer/index'
 
  console.log(V);
  • 简化命名空间
  namespace A  {
    export namespace B {
      export const C = 1
    }
  }
 
  import X = A.B.C
 
  console.log(X);
  • 合并命名空间

重名的命名空间会合并

三斜线指令

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

/// '<'reference path="..."/'>' 指令是三斜线指令中最常见的一种。 它用于声明文件间的 依赖。

三斜线引用告诉编译器在编译过程中要引入的额外的文件。

你也可以把它理解能import,它可以告诉编译器在编译过程中要引入的额外的文件

声明文件 declare

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

  declare var 声明全局变量
  declare function 声明全局方法
  declare class 声明全局类
  declare enum 声明全局枚举类型
  declare namespace 声明(含有子属性的)全局对象
  interface 和 type 声明全局类型
  /// <reference /> 三斜线指令

如果有一些第三方包确实没有声明文件我们可以自己去定义

名称.d.ts 创建一个文件去声明

例如express.d.ts

declare const express: ()=> any;

Mixins混入

TypeScript 混入 Mixins 其实vue也有mixins这个东西 你可以把他看作为合并

  • 对象混入

可以使用es6的Object.assign 合并多个对象

此时 people 会被推断成一个交差类型 Name & Age & sex;

  interface Name {
    name: string
  }
  interface Age {
    age: number
  }
  interface Sex {
    sex: number
  }
 
  let people1: Name = { name: "小满" }
  let people2: Age = { age: 20 }
  let people3: Sex = { sex: 1 }
 
  const people = Object.assign(people1,people2,people3)
  • 类的混入

Object.getOwnPropertyNames()可以获取对象自身的属性,除去他继承来的属性,
对它所有的属性遍历,它是一个数组,遍历一下它所有的属性名

  class A {
    type: boolean = false;
    changeType() {
        this.type = !this.type
    }
  }
  class B {
    name: string = '张三';
    getName(): string {
        return this.name;
    }
  }
  class C implements A,B{
    type:boolean
    changeType:()=>void;
    name: string;
    getName:()=> string
  }
  Mixins(C, [A, B])
  function Mixins(curCls: any, itemCls: any[]) {
    itemCls.forEach(item => {
      Object.getOwnPropertyNames(item.prototype).forEach(name => {
        curCls.prototype[name] = item.prototype[name]
        })
    })
  }

装饰器Decorator

  • 装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。

首先定义一个类

  class A {
    constructor() {
 
    }
  }

定义一个类装饰器函数 他会把ClassA的构造函数传入你的watcher函数当做第一个参数

  const watcher: ClassDecorator = (target: Function) => {
    target.prototype.getParams = <T>(params: T):T => {
        return params
    }
  }

使用的时候 直接通过@函数名使用

  @watcher
  class A {
    constructor() {
 
    }
  }

验证

  const a = new A();
  console.log((a as any).getParams('123'));
  • 装饰器工厂

其实也就是一个高阶函数 外层的函数接受值 里层的函数最终接受类的构造函数

  const watcher = (name: string): ClassDecorator => {
    return (target: Function) => {
      target.prototype.getParams = <T>(params: T): T => {
        return params
      }
      target.prototype.getOptions = (): string => {
        return name   
      }
    }
  }
 
  @watcher('name')
  class A {
    constructor() {
    }
  }
 
  const a = new A();
  console.log((a as any).getParams('123'));
  • 方法装饰器

返回三个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符。
  [
    {},
    'setParasm',
    {
      value: [Function: setParasm],
      writable: true,
      enumerable: false,
      configurable: true
    }
  ]
  const met:MethodDecorator = (...args) => {
    console.log(args);
  }
 
  class A {
    constructor() {
 
    }
    @met
    getName ():string {
      return '小满'
    }
  }
 
  const a = new A();
  • 属性装饰器

返回两个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 属性的名字。
  const met:PropertyDecorator = (...args) => {
    console.log(args);
  }
 
  class A {
    @met
    name:string
    constructor() {
 
    }  
  }
 
  const a = new A();
  • 参数装饰器

返回三个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。
  const met:ParameterDecorator = (...args) => {
    console.log(args);
  }
 
  class A {
    constructor() {
 
    }
    setParasm (@met name:string = '213') {
 
    }
  }
 
  const a = new A();

TS发布订阅模式

  • 具体代码

on订阅/监听

emit 发布/注册

once 只执行一次

off解除绑定

  interface EventFace {
    on:(name:string,callback:Function) => void
    emit:(name:string,...args:Array<any>) => void
    off:(name:string,fn:Function) => void
    once:(name:string,fn:Function) => void
  }
  interface List {
    [key:string]:Array<Function>
  }
  class Dispatch implements EventFace {
    list:List
    constructor(){
      this.list = {}
    }
    on(name:string,callback:Function){
      const callbackList:Array<Function> = this.list[name] || []
      callbackList.push(callback)
      this.list[name] = callbackList
    }
    emit(name:string,...args:Array<any>){
      let eventName = this.list[name]
      if (eventName) {
        eventName.forEach(fn=>{
          fn.apply(this,args)
        })
      } else {
        console.log("该事件未监听");
      }
    }
    off(name:string,fn:Function){
      let eventName = this.list[name]
      if (eventName && fn) {
        eventName.forEach(fn => {
          let index = eventName.findIndex(fns=> fns === fn)
          eventName.splice(index,1)
        })
      } else {
        console.log("该事件未监听");
      }
    }
    once(name:string,fn:Function){
      let decor = (...args:Array<any>) =>{
        fn.apply(this,args)
        this.off(name,decor)
      }
    }
  }

const o = new Dispatch()
 
 
o.on('abc', (...arg: Array<any>) => {
    console.log(arg, 1);
})
 
o.once('abc', (...arg: Array<any>) => {
    console.log(arg, 'once');
})
// let fn = (...arg: Array<any>) => {
//     console.log(arg, 2);
// }
// o.on('abc', fn)
// o.on('ddd', (aaaa: string) => {
//     console.log(aaaa);
// })
//o.off('abc', fn)
 
o.emit('abc', 1, true, '小满')
 
o.emit('abc', 2, true, '小满')
 
// o.emit('ddd', 'addddddddd'

Proxy & Reflect

Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

  • target
    要使用Proxy包装的目标对象(可以使任何类型的对象,包括原生数组,函数,甚至是一个代理)
  • handler
    一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作的代理p行为
    handler.get()本次使用的get
    属性读取操作的捕捉器
    handler.set()本次使用的set
    属性设置操作的捕捉器

Reflect与大多数全局对象不同,Reflect并非是一个构造函数,所以不能通过new运算符对其进行调用,或者将Reflect对象作为一个函数来调用。
Reflect的所有的属性和方法都是静态的(就像Math对象)

  • Reflect.get(target,name,receiver)
    Reflect.get方法查找并返回target对象的name属性,如果没有该属性返回undefined
  • Reflect.set(target,name,value,receiver)
    Reflect.set方法设置target对象的name属性等于value
  type Person = {
    name:string,
    age:number,
    text:string
  }
  const proxy = (object:any,key:any) => {
    return new Proxy(object,{
      get(target,prop,receiver){
        return Reflect.get(target,prop,receiver)
      },
      set(target,prop,value,receiver){
        return Reflect.set(target,prop,value,receiver)
      }
    })
  }
  const logAcess = (object:Person,key:'name'|'age'|'text') =>{
    return proxy(object,key)
  }
  let man:Person = logAcess({
    name:"1",
    age:20,
    text:"111"
  },age)
  man.age = 30
  console.log(man)

使用泛型+keyof优化

  type Person = {
    name:string,
    age:number,
    text:string
  }
  const proxy = (object:any,key:any) => {
    return new Proxy(object,{
      get(target,prop,receiver){
        return Reflect.get(target,prop,receiver)
      },
      set(target,prop,value,receiver){
        return Reflect.set(target,prop,value,receiver)
      }
    })
  }
  const logAcess = <T>(object:T,key:keyof T):T =>{
    return proxy(object,key)
  }
  let man:Person = logAcess({
    name:"1",
    age:20,
    text:"111"
  },age)
  man.age = 30
  console.log(man)

Partial & Pick

TypeScript内置高级类型Partial、Pick
Partial
看一下源码

  type Partial<T> = {
    [P in keyof T]? : T[P]
  }

使用前

  type Person = {
    name:string,
    age:number
  }
 
  type p = Partial<Person>

使用后

  type p = {
    name?: string | undefined;
    age?: number | undefined;
  }
  • keyof 是干什么的?

  • in 是干什么的?

  • ? 是将该属性变为可选属性

  • T[P] 是干什么的?

  1. keyof我们讲过很多遍了 将一个接口对象的全部属性取出来变成联合类型

  2. in 我们可以理解成for in P 就是key 遍历 keyof T 就是联合类型的每一项

  3. ?这个操作就是将每一个属性变成可选项

  4. T[P] 索引访问操作符,与 JavaScript 种访问属性值的操作类似

Pick
从类型定义T的属性中,选取指定一组属性,返回一个新的类型定义

  /**
 * From T, pick a set of properties whose keys are in the union K
 */
  type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
  };
  type Person = {
    name:string,
    age:number,
    text:string
    address:string
  }
 
  type Ex = "text" | "age"
 
  type A = Pick<Person,Ex>

Record & Readonly

Readonly

  type Readonly<T> = {
    readonly [P in keyof T]: T[P];
  };

Record

  type Record<K extends keyof any, T> = {
    [P in K]: T;
  };

infer

infer是TypeScript新增到的关键字充当占位符
我们来实现一个条件类型推断的例子
定义一个类型如果是数组类型,就返回数组元素的类型,否则就传入什么类型就返回什么类型

  type Infer<T> = T extends Array<any> ? T[number] : T
  type A = Infer<(boolean|string)[]>
  type B = Infer<null>

使用infer修改

  type Infer<T> = T extends Array<infer U> ? U : T

例子2配合tuple转换union联合类型

  type TupleToUni<T> = T extends Array<infer E> ? E :never
  type TTuple = [string,number]
  type ToUnion = TupleToUni<TTuple>

提取头部元素

  type Arr = ['a','b','c']
  type First<T extends any[]> = T extends [infer First,...any[]] ? First : []

提取尾部元素

  type Arr = ['a','b','c']
  type First<T extends any[]> = T extends [...any[],infer Last] ? Last : []

剔除第一个元素Shift

  type Arr = ['a','b','c']
  type First<T extends any[]> = T extends [unknown,...infer Rest] ? Rest : []
  type a = First<Arr>

剔除尾部元素pop

  type Arr = ['a','b','c']
  type First<T extends any[]> = T extends [...infer Rest,unknown] ? Rest : []
  type a = First<Arr>

infer递归

  type Arr = [1,2,3,4]
  type ReveArr<T extends any[]> = T extends [infer First,...infer rest] ? [...ReveArr<rest>,First] : T 
  type Res = ReveArr<Arr>
posted @ 2022-09-14 22:33  YaSuoKing082  阅读(65)  评论(0)    收藏  举报