更加完善的 TS 教程:https://ts.xcatliu.com/

基础认知

基本类型

number、NaN(Not a Number)
string
boolean
undefined
null
unknown
any
void

虽然 Any 和 unknown 都可以表示不确定的类型,但是 Any 是可以添加对象参数属性的,而 unknown 则不能。
ts 中尽量少用 Any。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let p1: any = {}
p1.name = '江湖浪子'

let p2: unknown = {}
// 报错
// p2.name = ''

// 都可以修改值
let p3: any = "金风玉露一相逢"
p3 = "便胜却人间无数"
let p4: unknown = "千里姻缘一线牵"
p4 = 2.36

console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);

联合类型

命名变量时使用 | 作为分隔符,写为其它类型,对该变量赋值时可以使用这几种类型的任何一种。

1
2
let p1: number|string = 123
let p2: number|string = "诗酒趁年华"

类型断言

将不确定的类型断言为确定的类型,类似 Java 中的强转。

有时,我们需要获取特定类型中的属性,返回值若是联合类型或者 Any,则该属性无法正常获取,编译会报错,这时我们可以采取以下方式:

1
2
3
4
5
6
7
// 方式一
let img = document.getElementById("#xxx") as HTMLImageElement
img.src = ""

// 方式二
let img = <HTMLImageElement>document.getElementById("#xxx")
img.height

数组类型

数组一般都是同类型的。

杂乱数组/泛型数组

数组中有多种不同类型的元素。

1
2
let arr1: any[] = ["仰天大笑出门去", 9981, false]
let arr2: (string|number|boolean)[] = [true, "123", 456]

多维数组

1
let arr: number[][] = [[1], [12]]

还有另一种写法,但是不够通用,这里略过。

类数组

元组

元组的优点是防止越界。

1
2
3
4
5
6
let arr1: [number, string, boolean] = [1, "true", false]
console.log(arr1[0]);
console.log(arr1[1]);
console.log(arr1[2]);
// 报错
// console.log(arr1[3]);

接口

接口定义类信息,类似 Java 中的 class,存在只读属性、可选属性,默认属性必须选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface UserInfo {
// 只读属性,不能修改
readonly id: number
name: string
age: number
// ? 可选属性
address?: string
like?: string[]
}

let user: UserInfo = {
name: "jhlz"
age: 18
}
user.name = "江湖浪子"
user.address = "上下五千年"
// 报错
// user.id = 2
console.log(user);
console.log(user.name);

接口中定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface UserInfo {
name: string
age?: number
// 定义函数
getName: () => string
setAge: (age: number) => void
}

let user: UserInfo = {
name: "江湖浪子",
getName() {
return this.name
},
setAge(age) {
this.age = age
}
}

user.setAge(18)

console.log(user);
console.log(user.name);

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface User {
// 1 男 2 女
gender: number
}

interface UserInfo extends User {
name: string
age?: number
}

let user: UserInfo = {
name: "江湖浪子",
// 必须给 gender 赋值
gender: 1
}

接口交叉类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface User {
// 1 男 2 女
gender: number
}

interface UserInfo {
name: string
age?: number
// 1 男 2 女,把这里的 number 改为其它类型将报错
gender: number
}

let user: UserInfo & User = {
name: "江湖浪子",
gender: 1
}

console.log(user);

交叉类型仅合并属性,而不处理属性的值或类型。如果两个接口中的相同属性具有不同的类型,代码将会报错。如果需要处理相同属性类型不同的情况,可以使用联合类型或其他适当的方法来处理。

类型别名

这个简单常用,给某些类型、函数定义别名,也可以用来定义对象。

定义对象

1
2
3
4
5
6
7
8
9
10
11
12
type infoType = {
readonly id : number
name: string
age?: number
address?: string
}

let info: infoType = {
id: 1,
name: "江湖浪子"
}
console.log(info);

interface 可以继承,type 不可以,只可以使用交叉类型;
interface 遇到重复命名的会自动合并;而 type 不会(直接报错);(实际项目中基本不会这样使用)

定义函数

1
2
3
4
5
6
7
8
// 定义函数类型
type fn = (para1: number, para2: number) => number

let sum: fn = (num1: number, num2: number) => num1 + num2
let diff: fn = (num1: number, num2: number) => num1 - num2

console.log(sum(1, 2));
console.log(diff(10, 3));

枚举

默认为数字枚举,并且从 0 开始增长,如果中间中断,那往下会继续从中断的地方自动增长。

1
2
3
4
5
6
7
8
9
enum Color {
RED,
BLUE,
GREEN,
YELLOW = 10000,
BLACK
}
console.log(Color.RED); // 0
console.log(Color.BLACK); // 10001

字符串枚举

1
2
3
4
5
6
7
8
enum Color {
RED="red",
BLUE="blue",
GREEN="green",
// 支持混合枚举,这里提一下
// YELLOW = 10000
}
console.log(Color.RED);

接口枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface ColorInterface {
color: Color,
blue: Color.BLUE
}
enum Color {
RED,
BLUE,
GREEN,
YELLOW = 10000,
BLACK
}
let c: ColorInterface = {
color: Color.RED,
blue: Color.BLUE
}

console.log(c);

const 枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface ColorInterface {
color: Color,
blue: Color.BLUE
}
const enum Color {
RED,
BLUE,
GREEN,
YELLOW = 10000,
BLACK
}

let c: ColorInterface = {
color: Color.RED,
blue: Color.BLUE
}

console.log(c);

主要作用就是节省因为枚举带来的资源消耗。

例如上面的接口枚举 ts 代码编译后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
var Color;
(function (Color) {
Color[Color["RED"] = 0] = "RED";
Color[Color["BLUE"] = 1] = "BLUE";
Color[Color["GREEN"] = 2] = "GREEN";
Color[Color["YELLOW"] = 10000] = "YELLOW";
Color[Color["BLACK"] = 10001] = "BLACK";
})(Color || (Color = {}));
var c = {
color: Color.RED,
blue: Color.BLUE
};
console.log(c);

而使用 const 枚举编译后:

1
2
3
4
5
var c = {
color: 0 /* Color.RED */,
blue: 1 /* Color.BLUE */
};
console.log(c);

泛型

泛型的好处大家也都了解,每种语言基本都会提供泛型,使用方式也都差不多。先声明后使用。

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 array<T>(a: T, b: T):T[] {
return [a, b]
}

function array<T extends string>(a: T, b: T): T[] {
console.log(a.length);
return [a, b]
}

function array<T, E>(a: T, b: E):(T|E)[] {
return [a, b]
}



interface User {
name: string
age: number
}

let user: User = {
name: "江湖浪子",
age: 18
}

// 泛型约束:T 只能是 User 接口中的属性
function getValue<T extends keyof User>(u: User, key: T): User[T] {
console.log(u[key]);
return u[key]
}
getValue(user, "age")

内置对象

ECMAScript

DOM

常见的 html 标签都在 HTMLElementTagNameMap 接口中。

Location
Storage
localStorage
sessionStorage
document.cookie(string 类型)

tsconfig.json

生成该文件的命令:tsc -init

该文件的说明可以查阅 官网,也可以百度网上查询,都有很多详解。下面也是从网上找的部分常用的配置项说明:

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
{
// ...
"compilerOptions": {
"target": "ES6", // 目标语言的版本
"module": "ES6", // 生成代码的模板标准
// "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": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
"removeComments":true, // 删除注释

"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, // 检查未使用的函数参数(只提示不报错)

"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { // 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
}
}

声明文件 d.ts

声明全局的变量和函数等

1
2
3
declare var Hello:string

declare function getValue<T>(param : T):string

装饰器

装饰器是 ES7 提出的实验性功能,默认是关闭的,可以使用以下选项进行开启:

tsconfig.json
1
2
"experimentalDecorators": true,
"emitDecoratorMetadata": 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
26
27
28
const app: ClassDecorator = (fn) => {
console.log(fn)
fn.prototype.userName = "江湖浪子"
}
@app
class User {

}
let u = new User()
console.log((u as any).userName)

// 函数柯里化
const app = (str: string): ClassDecorator => {
return (fn) => {
fn.prototype.userName = str
fn.prototype.getUserName = () => {
console.log(fn.prototype.userName)
}
}
}

@app("jhlz")
class User {

}

let u = new User()
console.log((u as any).userName)

本站由 江湖浪子 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。