# TypeScript 学习笔记
TypeScript 的类型校验是给程序员看的,在编译后不会存在TS代码。
# 语言类型分类
分为静态类型、动态类型和弱类型、强类型。 1.静态类型:编译期就知道每一个变量的类型。类型错误编译失败是语法问题。如 Java、C++。 2.动态类型:编译期不知道类型,运行时才知道。类型错误抛出异常发生在运行时。如 JS、Python。 3.弱类型:容忍隐式类型转换。如 JS,1+'1'='11',数字型转成了字符型。 4.强类型:不容忍隐式类型转换。如 Python,1+'1' 会抛出 TypeError。
# 类型注解
function fn(person: string):void{ // 参数类型是字符串,没有返回值
///...
}
fn('str') // 如传递参数非字符串,vscode编辑器中或在编译时将给出错误提示
const test:number = 1
1
2
3
4
5
6
2
3
4
5
6
# 有哪些基础类型注解?
// 基础类型
:string
:number
:boolean
:null // 只能是null值
:undefined // 只能是undefined值
:symbol
// 引用类型
:object // 不常用,多使用接口来给对象进行类型注解
// 其他
:any // 任意类型
:void // 空,用于函数的无返回值的注解
:never // 用于标注函数代码永远执行不完(如抛出错误的函数,死循环函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 什么是类型注解和类型推断、类型断言?
类型注解 是显式的标注类型
类型推断 是编辑器根据值来自动推断出类型 (编辑器中鼠标移到变量会显示类型的提示)。
类型断言 是告诉编译器,“相信我,它就是这个类型”。
// 类型注解就是显式的写出类型
const myNumber: number = 123
// 类型推断是根据值来推断类型 (鼠标移到变量会显示类型(或直接显示值)的提示)
const myString = 'test'
// 类型断言(开发者明确知道是什么类型)
const someValue:any = 'abc'
const strLength:number = (someValue as string).length // 断言someValue是字符串类型
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 对象的类型
// 对象字面量的类型检查
const xiaojiejie: {
name: string,
age: number
} = {
name: '小红',
age: 18
}
// 标注:object
const obj:object = {}
// 使用接口
interface Person{
name: string
age: number
}
const xjj: Person = {
name: 'xh',
age: 18
}
// class类的类型检查
class Parson { }
const xiaobai: Parson = new Parson()
// 函数和返回值的类型检查
const fn: ()=> string = () => '123'
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
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
# 函数的类型注解
// 函数返回值的类型注解 fn(): number {}
function getTotal2(one: number, two: number): number {
return one + two
}
getTotal2(1,2)
// 无返回值: void
function sayHello(): void {
console.log("Hello")
}
// 永远执行不玩的函数
function setTimer():never {
throw new Error()
console.log(123)
}
// 参数是对象时的注解 (也可以用接口)
function add({ one, two }: {one: number, two: number}) {
return one + two
}
const total = add({one: 1, two: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 数组的类型注解
const numberArr: number[] = [1, 2, 3]
const stringArr: string[] = ['a', 'b']
const undefinedArr: undefined[] = [undefined, undefined]
const arr: (number | string | boolean)[] = [1, 'a', true, false] // 用到联合类型
// 类型别名 type alias
type lady = { name: string, age: number }
const xiaojj: lady[] = [
{
name: 'xiaojj',
age: 90
},
{
name: 'j',
age: 30
}
]
// 或用类的方式
class Ady2 {
name: string;
age: number;
}
const xiaojj2: Ady2[] = [
{
name: 'xiaojj',
age: 90
},
{
name: 'j',
age: 30
}
]
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
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
# 元组
元组,可以理解为:已知元素数量和类型的数组
// 联合类型
const xjj:(string | number)[] = ['a',22,'b'] // 规定整个数组当中可以有string或number
// 元组注解 注意这里的注解只有一个中括号
const xjj1: [string, number, number] = ['a', 22, 33] // 规定了数组每个元素对应位置的类型
// Note: 在开发中元祖的使用在相对少
// CSV的方式定义数据格式; (二维数组时需要多加一个中括号)
const xjj2: [string, number, number][] = [
['a', 22, 33],
['a', 22, 33]
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 接口
接口,可以理解为对象属性的类型描述。和类型别名
类似,不同的是 接口必须是一个对象,而别名可以直接是一个类型,如type Girl = string
interface Girl { // 接口 (理解:对象属性的类型描述)
readonly name: string; // 只读属性 (定义之后不能再修改)
age: number;
waistline?: number; // 加个问号表示是可选值
[propname: string]: any; // 表示可以有不限制属性名的属性,但属性名需要是字符串,值可以是任意类型
say(): string; // 函数类型,返回值是string (如无返回值时是 void)。 say()也可以加入参数类型检查,如say(p:number)
}
// 和类型别名类似,不同的是 接口必须是一个对象,而别名可以直接是一个类型,如 type Girl = string
// 接口的继承
interface Teacher extends Girl {
teach(): string;
}
const girl = {
name: '大脚',
age: 18,
sex: '女',
say() {
return '欢迎光临'
},
teach() {
return '教'
}
}
const screenResume = ({ name, age, bust, sex }: Girl) => {
console.log(name, age, bust, sex)
}
const getResume = ({ name, age, bust, teach}: Teacher) => {
teach();
console.log(name, age, bust)
}
screenResume(girl)
getResume(girl)
// 接口在class类中的使用
class xiaojjj implements Girl {
name = "xiaojj"
age = 18
bust = 98
sex = '女'
say() {
return '欢迎光临'
}
}
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
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
可索引的类型
interface Arr {
[index: number]:string // 表示通过索引访问数据时返回的类型是string
}
const myArr:Arr = ['1','2','3']
const myStr:string = myArr[1]
1
2
3
4
5
6
2
3
4
5
6
# ES6 class类中应用TS
# class类的修饰器
// 类的修饰器:
// public (公共) 允许内外部使用
// protected (受保护的) 允许在内部和继承内使用
// private (私有) 允许在内部使用,但不能在继承中使用
// 类的内部和外部,{}内属于内部,外面是外部
class Person {
// public 内外都可以使用,可省略,不写时默认public。 protected只允许内部使用
name: string // 这里的string注释是TS使用的
private age: 18
public sayHello() {
console.log(this.name + this.age + 'say hello')
}
}
class Teacher2 extends Person {
public sayBye() {
console.log(this.name + ' say bye') // protected 可以在继承中使用到
}
}
const person = new Person();
person.name = 'test' // 在类的外部定义
console.log(person.name)
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# class类的构造函数中使用类型校验
class Person2 {
constructor(public name: string) {
this.name = name
}
}
class Teacher3 extends Person2{
constructor(public age: number) {
super('test-name') // 这里传的值是给Person2的构造函数。即使父类没有构造函数,子类的构造函数内也要写super()
}
}
const teacher3 = new Teacher3(18)
console.log(teacher3.name)
console.log(teacher3.age)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# class类的getter、setter和static
class Xjj {
constructor(private _age: number) { }
get age() {
return this._age - 10;
} // 访问器属性,以属性的形式访问age,并可以对属性进行包装
set age(age: number) {
this._age = age+3;
}
}
const dj = new Xjj(28);
dj.age = 25
console.log(dj.age)
// 静态属性 static。 静态属性指不需要通过实例化,直接通过Girl.prop的方式就可以访问到属性
class Girl {
static sayLove() {
return 'I love you'
}
}
// const girl = new Girl()
console.log(Girl.sayLove())
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
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
# class类的只读属性
// 只读属性
class Person {
public readonly _name: string // 只读属性
constructor(name: string) {
this._name = name;
}
}
const person = new Person('testName');
// person._name = '222'; // 不能修改只读属性
console.log(person._name)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 抽象类
/ 抽象类
abstract class Girls {
abstract skill(); // 注意这里只是定义抽象方法,而不具有方法的实现
}
class Waiter extends Girls{ // 继承了抽象类之后要 实现抽象类内的成员
skill() {
console.log('大爷1')
}
}
class BaseTeacher extends Girls{
skill() {
console.log('大爷2')
}
}
class SeniorTeacher extends Girls{
skill() {
console.log('大爷3')
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 联合类型和类型保护
联合类型 指某个参数可以是多种类型。
类型保护 指参数属于某个类型才有相应的操作。
interface Waiter {
anjiao: boolean
say: () => {}
}
interface Teacher {
anjiao: boolean
skill: () => {}
}
function judgeWho(animal: (Waiter | Teacher)) { // 联合类型
// 第一种断言方法
if (animal.anjiao) {
// (animal as Teacher) 的意思是:断言 animal 是 Teacher类型
(animal as Teacher).skill()
} else {
(animal as Waiter).say()
}
// 第二种断言方法
if ('skill' in animal) {
animal.skill()
} else {
animal.say()
}
// 第三种类型保护方法是使用typeof来判断 (代码省略)
}
class NumberObj {
count: number
}
function addObj(first: object | NumberObj, second: object | NumberObj) { // 联合类型
if (first instanceof NumberObj && second instanceof NumberObj) { // 类型保护
return first.count + second.count;
}
return 0;
}
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
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
# 枚举
// Enum枚举类型 (个人理解枚举:约定一组可选的常量。 使用常量名表示某个值的含义,增强可读性。)
// js写法
// const Status = {
// MASSAGE: 0,
// SPA: 1,
// DABAOJIAN: 2
// }
// ts写法
enum Status {
MASSAGE, // 如果想从1开始,给MASSAGE = 1, 即可
SPA,
DABAOJIAN
} // 默认赋值 0 、1、2
console.log(Status.MASSAGE, Status[0]) // 0, MASSAGE 可以通过下标反查
function getStatus(status: any) {
if (status === Status.MASSAGE) {
return 'massage'
} else if (status === Status.SPA) {
return 'spa'
} else if (status === Status.DABAOJIAN) {
return 'dabaojian'
}
}
const result = getStatus(Status.SPA)
console.log(result)
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
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
笔记
PS:官网上有这么一句话:在现代TypeScript
中,我们或许不太需要枚举类型,因为可以通过对一个对象使用常量断言 as const
来替代枚举。甚至用对象的方式可能会更好,因为更贴近JS
语言习惯。
const enum EDirection {
Up,
Down,
Left,
Right,
}
const ODirection = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
// 当 as const 修饰符用在变量声明或表达式的类型上时,它会强制 TypeScript 将变量或表达式的类型视为不可变的(immutable)。这意味着,如果你尝试对变量或表达式进行修改,TypeScript 会报错。
const foo = ['a', 'b'] as const; // 等价于 const foo: ['a', 'b'] = ['a', 'b']
EDirection.Up; // 0
ODirection.Up; // 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 几种常用的ts枚举类型的操作技巧:
enum Status {
SUCCESS = 'success',
DANGER = 'danger',
WARNING = 'warning',
}
1
2
3
4
5
2
3
4
5
# 1. 获取枚举的 key 类型
type StatusKey = keyof typeof Status;
// 'SUCCESS' | 'DANGER' | 'WARNING'
const keyArr: StatusKey[] = ['SUCCESS', 'DANGER']; // passed
1
2
3
4
2
3
4
# 2. 获取枚举的 value 类型
type StatusVal = `${Status}`;
// 'success' | 'danger' | 'warning'
const valArr: StatusVal[] = ['success', 'danger', 'warning']; // passed
1
2
3
4
2
3
4
# 3. 合并枚举类型
enum Wait {
WAITING = 'waiting',
}
const merge = { ...Status, ...Wait };
type MergeStatus = typeof merge;
const s1 = MergeStatus.WAITING; // warning
const s2 = MergeStatus.SUCCESS; // success
const s3 = MergeStatus.DANGER; // danger
const s4 = MergeStatus.WARNING; // warning
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 4. 使用 Exclude 剔除枚举中的值
type ErrStatus = Exclude<Status, Status.SUCCESS>;
let a: ErrStatus = Status.DANGER; // passed
a = Status.WARNING; // passed
a = Status.SUCCESS; // error
1
2
3
4
5
2
3
4
5
# 泛型
泛型,最简单的理解:泛指的类型。(类似函数中的形参与实参)
# 函数中的泛型使用
// function join(first: string | number, second: string | number) {
// return `${first}${second}`
// }
// join('jspang', 1); // 如果我想第一个参数是字符串,第二个也必须是字符串,这么就用到泛型
// 泛型使用,如同定义形参,在调用时指定类型
function join<JSPang>(first: JSPang, second: JSPang) {
return `${first}${second}`
}
join<string>('jspang', '123');
join<number>(11, 22);
// 泛型中数组的使用
function myFun<ANY>(params:ANY[]) { // ANY[] or Array<ANY>
return params
}
myFun<string>(['a', 'b'])
// 两个类型参数的使用(工作中,常用T表示泛型)
function join2<T,P>(first: T, second: P) {
return `${first}${second}`
}
join2<string,number>('jspang', 123);
join2<number, string>(11, '22');
join2(11, '22'); // 泛型也支持类型推断 (鼠标移到函数名有提示)
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
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
# class类中使用泛型
// class SelectGirl {
// constructor(private girls: string[] | number[]) { } // private 私有的参数,外部无法修改
// getGirl(index: number): string | number {
// return this.girls[index]
// }
// }
// 使用泛型
class SelectGirl<T> { // 泛型的约束: <T extends number | string>
constructor(private girls: T[]) { } // private 私有的参数,外部无法修改
getGirl(index: number): T {
return this.girls[index]
}
}
// const selectGirl = new SelectGirl<string>(['大脚', 'xiaohong', 'xiaobai'])
const selectGirl = new SelectGirl<number>([101, 102, 103])
console.log(selectGirl.getGirl(1))
// 泛型中的继承
interface Girl {
name: string
}
class SelectGirl2<T extends Girl> { // 泛型T中必须有一个name属性,继承自Girl接口
constructor(private girls: T[]) { } // private 私有的参数,外部无法修改
getGirl(index: number): string {
return this.girls[index].name
}
}
const selectGirl2 = new SelectGirl2([
{name: '大脚1'},
{name: '大脚2'},
{name: '大脚3'}
])
console.log(selectGirl2.getGirl(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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
# 配置文件tsconfig.json
// 此文件由命令 tsc -init 生成
// 直接运行 tsc 命令就会运用此配置文件
// 选项详解:https://www.tslang.cn/docs/handbook/compiler-options.html
{
// "include": ["demo15-1.ts"], // 要编译的指定文件,不配置此项时运行tsc默认编译全部
// "files": ["demo15-1.ts"], // 和include类似
// "exclude": ["demo15-3.ts"], // 要排除编译的指定文件
"compilerOptions": { // 编译选项
/* 基本选项 */
// "incremental": true, /* 启用增量编译 */
"target": "es5", /* 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* 指定模块代码生成: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* 指定要包含在编译中的库文件. */
// "allowJs": true, /* 允许编译javascript文件*/
// "checkJs": true, /* 报告.js文件中的错误。 */
// "jsx": "preserve", /* 指定 JSX代码生成: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* 生成相应的 .d.ts文件 */
// "declarationMap": true, /* 为每个相应的 .d.ts文件生成一个sourcemap (编译后代码对源码的映射)*/
"sourceMap": true, /* 生成源文件与输出文件的映射关系文件(.map)。*/
// "outFile": "./", /* 将输出文件合并为一个文件. */
"outDir": "./build", /* 输出的js文件目录。 */
"rootDir": "./src", /* ts源文件目录。 */
// "composite": true, /* 启用 项目编译 */
// "tsBuildInfoFile": "./", /* 指定用于存储增量编译信息的文件 */
// "removeComments": true, /* 不输出注释到编译结果.(删除所有注释,除了以 /!*开头的版权信息。) */
// "noEmit": true, /* 不发出输出.Do not emit outputs. */
// "importHelpers": true, /* 从“tslib”导入发出助手. Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* 将每个文件作为单独的模块(与“ts.transpileModule”类似) */
/* 严格的类型检查选项 */
"strict": true, /* 启用所有严格类型检查选项。 打开此选项后,下面这些选项就不需要单独设置*/
// "noImplicitAny": true, /* 对隐含的“any”类型的表达式和声明引发错误. (为false时允许any不用特意声明)*/
// "strictNullChecks": true, /* 启用严格的null检查. (为false时允许赋值为null)*/
// "strictFunctionTypes": true, /* 启用严格检查函数类型. */
// "strictBindCallApply": true, /* 启用函数上严格的“bind”、“call”和“apply”方法. */
// "strictPropertyInitialization": true, /* 启用 严格检查类中的属性初始化. */
// "noImplicitThis": true, /* 在隐含的“any”类型的“this”表达式上引发错误。 */
// "alwaysStrict": true, /* 以严格模式解析并为每个源文件发出“use strict”。 */
/* 附加检查。Additional Checks */
// "noUnusedLocals": true, /* 若有未使用的局部变量则抛错 */
// "noUnusedParameters": true, /* 若有未使用的参数则抛错。 */
// "noImplicitReturns": true, /* 不是函数的所有返回路径都有返回值时报错。*/
// "noFallthroughCasesInSwitch": true, /* 报告switch语句的fallthrough错误。(即,不允许switch的case语句贯穿) */
/* 模块解析选项 */
// "moduleResolution": "node", /* 决定如何处理模块:'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* 用于解析非绝对模块名称的基础目录。 */
// "paths": {}, /* 模块名到基于 baseUrl的路径映射的列表。 */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* 要包含的类型声明文件名列表 */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* 启用s emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* 启用s experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* 启用s experimental support for emitting type metadata for decorators. */
}
}
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
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