TypeScript细化知识点2

2/7/2023 ts

# 7、类型断言 as

  • as
  • <A> type
  • 使用any临时断言,any可以断言成任意类型 window as any

# 8、内置对象

# DOM

  • NodeList
  • HTMLElement
  • ...
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;
}
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

# BOM

  • MouseEvent

# Promise

/**
 * promise
 * @returns Promise
 */
function promise(): Promise<number> {
    return new Promise<number>((resolve, reject) => {
        resolve(1);
    })
}

promise().then((res => {
    console.log(res);
}));
1
2
3
4
5
6
7
8
9
10
11
12
13

# 9、Class

/**
 * class
 * 
 */
class Person {
    public name: string = 'xxx' // public: 外部
    protected age: number // protected: 内部和子类中
    private sub: boolean  // private: 内部
    static aaa:string = '我是大帅哥' // static 静态属性,直接透过类就可以访问 Person.aaa
    constructor (name: string, age: number, sub: boolean) { // 不能调用静态方法
        this.name = name;
        this.age = age;
        this.sub = sub;
        // console.log(this.sub); // true
    }
    static run () {
        this.aaa = '你猜'; // 静态方法内只能访问静态属性和方法,不能访问内部变量和方法
        return '我真厉害';
    }
}

class Man extends Person {
    constructor() {
        super('cyh', 24, false); // super()调用父类的构造函数
        // console.log(this.age); // 24
        this.age = 23;
        
    }
}

let p = new Person('wyx', 23, true);
// console.log(p.name); // wyx

let m = new Man();
// console.log(m.name); // cyh

console.log(Person.aaa); // 我是大帅哥
console.log(Person.run()); // 我真厉害

// 使用接口去描述类
interface P {
    run(type: boolean): boolean
}

interface H {
    set(): void
}

class A {
    public params
    constructor(params) {
        this.params = params
    }
}

class M extends A implements P, H { // 实现接口
    run (type: boolean): boolean {
        return type
    }
    set () {

    }
}
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

# 10、抽象类

/**
 * 抽象类
 */
abstract class A3 {
    name: string
    constructor(name: string) {
        this.name = name;
    }
    setName(name: string): void {
        this.name = name;
    }
    abstract getName(): string
}

class B3 extends A3 {
    constructor() {
        super('wyx');
    }
    getName(): string { 
        // 定义的抽象方法必须在抽象类的派生类里面实现
        return this.name;
    }
}

let b = new B3();
console.log(b.getName()); // wyx
b.setName('xxx');
console.log(b.getName()); // xxx
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

# 11、元组(Tuple)类型

元组(Tuple)是固定数量的不同类型的元素的组合

let aArr:[string, number] = ['wyx', 123];

// aArr.push(true); // 报错  对于越界的元素他的类型被限制为 联合类型(就是你在元组中定义的类型)  

let excel: [string, string, number, string][] = [
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
]
1
2
3
4
5
6
7
8
9
10
11

# 12、枚举 enum

用来约束统一

enum sexType {
    BOY,
    GRIL
}
1
2
3
4

# 数字枚举

// 数字枚举
enum Num {
    one,
    two,
    three
}
// 默认从0开始
console.log(Num.one); // 0
console.log(Num.two); // 1
console.log(Num.three); // 2
// 数字增长枚举
enum Num2 {
    one = 2,
    two,
    three
}
console.log(Num2.one); // 2
console.log(Num2.two); // 3
console.log(Num2.three); // 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 字符串枚举

enum Str { // 全部都得赋值
    one = 'qqq',
    two = 'www',
    three = 'eee'
}
1
2
3
4
5

# 异构枚举

混合字符串和数字成员

enum Yg {
    a,
    b = 1,
    c,
    d = 'sss', // 字符串后面不能插入不赋值的,不能自动识别递增
    e = 5,
    f
}

console.log(Yg.a, Yg.c, Yg.f); // 0, 2, 6
1
2
3
4
5
6
7
8
9
10

# 接口枚举

声明对象的时候要遵循这个规则

   enum Types {
      yyds,
      dddd
   }
   interface A {
      red:Types.yyds
   }
 
   let obj:A = {
      red:Types.yyds
   }
1
2
3
4
5
6
7
8
9
10
11

# const枚举

let 和 var 都是不允许的声明只能使用const

# 反向映射

正向映射( name -> value

反向映射( value -> name

// 正向映射 key-value  反向映射 value-key
enum maps {
    as = 2,
    bs
}

let bm = maps.bs; // 正向映射
let bmKey = maps[bm]; // 反向映射

console.log(`key:${bmKey},value:${bm}`); // key:bs,value:3
1
2
3
4
5
6
7
8
9
10

# 13、类型推论|类型别名

  • 类型推论

    • TypeScript (opens new window) 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论
    • 一旦确定就不可以再赋值其他类型
    • 如果只是声明变量也没有对其进行初始化赋值,那么默认any类型
  • 类型别名

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

    • /**
       * 类型别名 type关键字
       */
      type str = string;
      let s:str = 'wxx';
      
      // 定义函数别名
      type fn = () => number;
      const fun:fn = () => 222;
      console.log(fun()); // 222
      
      // 联合类型
      type LT = string | number;
      let sn = 'wxi';
      let sn2 = 22;
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

# type 和 interface对比

  • 都可以定义类型
  • 不同:
    • interface可以继承 type 只能通过 & 交叉类型合并
    • type 可以定义 联合类型 和 可以使用一些操作符 interface不行
    • interface 遇到重名的会合并 type 不行

# type高级用法

// type 高级用法
// extends 包含的意思
// 左边的值 会作为后边类型的子类型
// any unknow
// Object
// Number String Boolean
// number string boolean
// never
// 从上到下,下面是上边的子类
type numo = [222] extends Object ? 1 : 0; // 1
type num = 1 extends never ? 1 : 0; // 0 判断1是不是never的子类,是-》1,都-》0
type numn = {name: 'wyx'} extends number ? 1 : 0; // 0
1
2
3
4
5
6
7
8
9
10
11
12

# 14、never类型

ts用never类型来表示不应该存在的状态

/**
 * never类型
 */
// 如果出现never类型,多半是逻辑出错了
type bbb = string & number // bbb = never

// 抛出异常
function error(message: string): never {
    throw new Error(message);
}

// 死循环
function loop(): never {
    while(true) {}
}

// 案例
interface A {
    type: "保安"
}

interface B {
    type: '保洁'
}

type AB = A | B;
function type(val: AB) {
    switch(val.type) {
        case '保安':
            break;
        case '保洁':
            break;
        default:
            const check:never = val;
            break;
    }
}

// 由于任何类型都不能赋值给 never 类型的变量,所以当存在进入 default 分支的可能性时,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

# never与void

  • void只是没有返回值不报错

  • never只会抛出异常没有返回值

# 15、Symbol 类型

/**
 * Symbol
 */
// 参数只支持string | number
const s1 = Symbol('str');
const s2 = Symbol(222);

// 用作对象key时
const objs = {
    [s1]: 'str',
    [s2]: 222,
    name: 'wyx',
    age: 32
}

// 以下遍历获取不到symbol值
for(let key in objs) {
    console.log(key); // name age
}

console.log(Object.keys(objs)); // [ 'name', 'age' ]
console.log(Object.getOwnPropertyNames(objs)); // [ 'name', 'age' ]
console.log(JSON.stringify(objs)); // {"name":"wyx","age":32}

// 以下可以获取到symbol值 
console.log(Object.getOwnPropertySymbols(objs)); // [ Symbol(str), Symbol(222) ]
// Reflect 拦截器
console.log(Reflect.ownKeys(objs)); // [ 'name', 'age', Symbol(str), Symbol(222) ]
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

# 16、泛型

# 泛型约束

// 泛型约束
// 我们期望在一个泛型的变量上面,获取其length参数,但是,有的数据类型是没有length属性的
interface Len {
    length: number
}
function getLength<T extends Len>(arg: T) {
    return arg.length;
}
console.log(getLength('sasdas')); // 6
1
2
3
4
5
6
7
8
9
  • keyof 约束对象
// 约束对象key——keyof
function prop<T, K extends keyof T>(obj: T, key: K) {
    return obj[key];
}
let o = {a: 1, b: 2, c: 3};
console.log(prop(o, 'a')); // 1
prop(o, 'd'); // 会报错显示没有d值
1
2
3
4
5
6
7

# 泛型类

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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 17、namespace命名空间

防止全局变量污染

TypeScript (opens new window)提供了namespace 避免这个问题出现

  • 内部模块,主要用于组织代码,避免命名冲突。
  • 命名空间内的类默认私有
  • 通过 export 暴露
  • 通过 namespace 关键字定义

包含import和export的文件被当作一个模块,反之它们的内容都被视为全局可见

  • 通过export作为模块导出

    • export const a5 = 1
      
      1
  • 通过namespace命名空间

    • // ts
      namespace A5 {
          export const a5 = 2;
      }
      
      console.log(A5.a5); // 2
      
      // js
      var A5;
      (function (A5) {
          A5.a5 = 2;
      })(A5 || (A5 = {}));
      console.log(A5.a5);
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13

# 嵌套命名空间

namespace a {
    export namespace b {
        export class Vue {
            parameters: string
            constructor(parameters: string) {
                this.parameters = parameters
            }
        }
    }
}
 
let v = a.b.Vue
 
new v('1')
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 抽离命名空间

  • a.ts

    • export namespace B {
          export const a5 = 3
      }
      
      1
      2
      3
  • b.ts

    • import {B} from './b';
      
      console.log(B.a5);
      
      1
      2
      3

# 简化命名空间

namespace A  {
    export namespace B {
        export const C = 1
    }
}
 
import X = A.B.C
 
console.log(X); // 1
1
2
3
4
5
6
7
8
9

# 合并命名空间

重名的命名空间会合并

namespace A {
    export const a = 1
}
namespace A {
    export const b = 2
}

// 相当于
namespace A {
    export const a = 1;
    export const b = 2;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 18、三斜线指令

三斜线指令可放在包含它的文件的最顶端

如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

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

///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
 
console.log(A);
1
2
3
4
  • 声明文件引用

    • /// <reference types="node" />
      
      1

# 19、声明文件d.ts

关键字 declare

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

# 20、Mixins混入

# 对象混入

interface A5 {
    name: string
}
interface B5 {
    age: number
}
interface C5 {
    sex: number
}

const student1: A5 = {name: 'wyx'};
const student2: B5 = {age: 23};
const student3: C5 = {sex: 1};

const student = Object.assign(student1, student2, student3);
console.log(student); // { name: 'wyx', age: 23, sex: 1 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 类混入

class Type {
    type: boolean = false
    changeType(): void {
        this.type = !this.type
    }
}
class Name {
    name: string = 'wyx'
    getName(): string {
        return this.name
    }
}
class TypeName implements Type,Name{
    type: boolean = false;
    changeType!: () => void; 
    name: string = 'wxx';
    getName!: () => string;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 封装函数

function Mixins(currClass: any, itemClass: any[]) {
    itemClass.forEach(item => {
        Object.getOwnPropertyNames(item).forEach(name => {
            currClass.prototype[name] = item.prototype[name];
        })
    })
}
Mixins(TypeName, [Type,Name]);
1
2
3
4
5
6
7
8

# 21、装饰器 Decorator (实验性特性)

  • 增加代码的可读性,清晰表达意图
  • 提供一种方便的手段,增加或修改类的功能

需要在命令行或tsconfig.json里启用编译器选项 "experimentalDecorators": true

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

装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

// 声明一个装饰器
const watcherWyx:ClassDecorator = (target: Function) => {
    target.prototype.getName = <T>(name: T):T => {
        return name;
    }
}

@watcherWyx
class A6 {

}

let a6 = new A6();

console.log((a6 as any).getName('wyx'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 装饰器工厂

定制一个修饰器如何应用到一个声明上


// 装饰器工厂,高阶函数
const watcherHigh = (name: string):ClassDecorator => {
    return (target: Function) => {
        target.prototype.getName = () => {
            return name;
        }
    }
}

@watcherHigh('xxx')
class B6 {

}

let b6 = new B6();

console.log((<any>b6).getName()); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 装饰器组合

多个装饰器可以同时应用到一个声明上

就是可以同时使用多个装饰器

  • 书写在同一行

    • @watcher1 @watcher2 class A {}
  • 书写在多行

    • @watcher1
      @watcher2
      class A {}
      
      1
      2
      3
  • 当多个装饰器应用在一个声明上时会进行如下步骤的操作:

    1. 由上至下依次对装饰器表达式求值。

    2. 求值的结果会被当作函数,由下至上依次调用。

    3. const watcherHigh = (name: string):ClassDecorator => {
          console.log('highw');
          return (target: Function) => {
              console.log('high');
              target.prototype.getName = () => {
                  return name;
              }
          }
      }
      
      const watcherCom = (age: number):ClassDecorator => {
          console.log('comw');
          return (target: Function) => {
              console.log('com');
              target.prototype.getAge = () => {
                  return age;
              }
          }
      }
      
      @watcherHigh('c6x')
      @watcherCom(23)
      class C6 {
          constructor() {
      
          }
      }
      
      let c6 = new C6();
      
      // 打印结果
      // highw comw com high
      
      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

# 属性装饰器:PropertyDecorator

# 方法装饰器:MethodDecorator

# 参数装饰器:ParameterDecorator

Last Updated: 4/18/2023, 2:49:49 PM
晴天
周杰伦