TypeScript细化知识点2
# 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;
}
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);
}));
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 () {
}
}
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
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'],
]
2
3
4
5
6
7
8
9
10
11
# 12、枚举 enum
用来约束统一
enum sexType {
BOY,
GRIL
}
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
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'
}
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
2
3
4
5
6
7
8
9
10
# 接口枚举
声明对象的时候要遵循这个规则
enum Types {
yyds,
dddd
}
interface A {
red:Types.yyds
}
let obj:A = {
red:Types.yyds
}
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
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
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的类型检查会及时帮我们发现这个问题
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) ]
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
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值
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')
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')
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
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;
}
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);
2
3
4
声明文件引用
/// <reference types="node" />
1
# 19、声明文件d.ts
关键字 declare
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
/// <reference /> 三斜线指令
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 }
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;
}
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]);
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'));
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());
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
当多个装饰器应用在一个声明上时会进行如下步骤的操作:
由上至下依次对装饰器表达式求值。
求值的结果会被当作函数,由下至上依次调用。
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