TypeScript 入门
0x01 概述
-
TypeScript 是一种基于 JavaScript 构建的强类型编程语言
- JavaScript 的超集,即 JavaScript 是 TypeScript 的子集
- JavaScript 类型化,即在 JavaScript 基础上添加类型系统
-
优势:
- 提供可读性和可维护性
- 在编译阶段检查并报错
-
环境:
- 安装 NodeJS
- 使用命令
npm install -g typescript
全局安装 TypeScript
-
编译与运行:
-
方案一:
-
创建 index.ts
let a: number = 123; console.log(a);
-
使用命令
tsc ./index.ts
编译 index.ts 文件,生成同名的 index.js 文件var a = 123; console.log(a);
-
使用命令
node ./index.js
运行生成的 index.js 文件
-
-
方案二:
- 使用命令
npm install -g ts-node
全局安装 ts-node 工具- 用于简化方案一的步骤,直接编译和运行 TypeScript 文件
- 使用命令
ts-node ./index.ts
编译和运行 index.ts 文件- 或使用 VSCode 的 Code Runner 插件
- 使用命令
-
0x02 类型
(1)基础数据类型
-
number
:数字let a: number = 123;
-
string
:字符串let a: string = "abc";
-
boolean
:布尔let a: boolean = true;
-
null
:空值let a: null = null;
-
undefined
:未定义let a: undefined = undefined;
-
symbol
:Symbollet a: symbol = Symbol("a");
-
void
:函数无返回值 -
never
:函数返回错误值
(2)复杂数据类型
-
对象:
type MyObj = { id: number; name?: string; }; let a: MyObj = { id: 1, name: "a" }; let b: MyObj = { id: 1 };
type
是声明类型别名的关键字
-
数组:
let a: number[] = [1, 2, 3]; let b: Array<number> = [1, 2, 3];
-
类:
class MyClass {} let a: MyClass = new MyClass();
-
函数:
let a: () => number = () => 1 + 2;
-
元组:
let a: [number, string] = [123, "abc"];
-
任意数据类型:
any
与unknown
(3)类型注解与类型推断
-
类型注解:在声明变量时,指定变量的数据类型
let a: number = 123; console.log(typeof a); // number
-
类型推断:在声明变量时,指定变量的值,由 TypeScript 编译器自动推断变量的数据类型
let a = 123; console.log(typeof a); // number
(4)函数类型注解
-
返回类型注解:
function a(): number { return 123; }
-
参数注解:
function b(num: number): number { return num; }
-
对象参数注解:
function c({ a, b }: { a: number; b: number }): number { return a + b; }
(5)类型符号
A | B
:联合类型,变量为 A 类型或 B 类型A & B
:交叉类型,变量为 A 类型且 B 类型(常用于对象类型)
(6)类型断言
-
尖括号:
let num: any = 123; console.log((<string>num).length); // undefined
-
as
关键字:let num: any = 123; console.log((num as string).length); // undefined
(7)操作符
-
!
:非空断言let a: string | null; console.log(a!.length);
-
?
:可选链操作符let a: { b?: { c?: number; }; } = {}; console.log(a?.b?.c); // undefined
-
??
:空值合并操作符let a: null = null; let b = a ?? 10; console.log(b);
- 与
||
区别在于:当左值为空值时采用右值,而||
的左值为假值时采用右值
- 与
(8)interface 对象类型
-
一种校验工具,并指定对象的各个属性
-
一般语法:
interface MyObj { id: number; name: string; } let a: MyObj = { id: 1, name: "a" };
-
可选属性:
?
interface MyObj { id: number; name?: string; } let a: MyObj = { id: 1 };
-
任意属性:
interface MyObj { id: number; [propName: string]: any; } let a: MyObj = { id: 1, name: "a" };
-
只读属性:
interface MyObj { readonly id: number; [propName: string]: any; } let a: MyObj = { id: 1, name: "a" }; a.id = 2; // 此语句报错:Cannot assign to 'id' beacause it is a read-only property.
(9)枚举类型
-
使用
enum
关键字声明枚举类型 -
举例一:颜色枚举
enum Color { RED = "#f00", GREEN = "#0f0", BLUE = "#00f", } console.log(Color.RED); console.log(Color.GREEN); console.log(Color.BLUE);
-
举例二:数值自增
enum Letter { a, b = 2, c, d, } console.log(Letter.a); // 0 console.log(Letter.b); // 2 console.log(Letter.c); // 3 console.log(Letter.d); // 4
0x03 类
(1)基本使用
-
TypeScript 兼容 ES6 和 ES7 关于类的使用方法
-
ES6 类:
class MyClass { constructor() { this.name = "ES6"; } getName() { return this.name; } }
-
ES7 类:
class MyClass { name; constructor() { this.name = "ES7"; } getName() { return this.name; } }
-
-
ES 中,继承父类的子类可以使用
super
关键字调用父类的方法class Parent { name = "Parent"; getName() { console.log("Parent: ", this.name); } } class Child extends Parent { name = "Child"; getName() { console.log("Child: ", this.name); // Child: Child super.getName(); // Parent: Child } } let child = new Child(); child.getName();
-
TypeScript 的类提供了三种访问修饰符:
public
、private
、protected
protected
的作用与private
类似,区别在于可以在继承子类被访问
class Parent { protected name = "Parent"; } class Child extends Parent { getName() { console.log("Child: ", this.name); } } let child = new Child(); child.getName(); // Child: Parent
(2)存取器
TypeScript 的类提供了 getter 修饰符 get
和 setter 修饰符 set
class MyClass {
private a: number;
constructor(a: number) {
this.a = a;
}
get aValue() {
return this.a;
}
set aValue(a: number) {
this.a = a;
}
}
let mc = new MyClass(1);
console.log(mc.aValue); // 1
mc.aValue = 2;
console.log(mc.aValue); // 2
(3)抽象类
-
使用
abstract
关键字定义抽象类 -
抽象类不可被实例化
-
举例:
abstract class Person { abstract call(): string; } class Student extends Person { call() { return "student"; } } let student = new Student(); console.log(student.call());
0x04 泛型
(1)概述
-
泛型指根据使用时的数据类型而定,对内类型同一,对外类型不定
-
泛型有利于方法、类的复用
-
举例:
function map<T>(arr: T[], fn: (item: T) => T) { return arr.map(fn); } let arr = map([1, 2, 3], (item) => item + 1); console.log(arr); // [ 2, 3, 4 ]
(2)泛型函数与泛型接口
-
泛型函数与非泛型函数:
// 泛型函数 let fn1: (arg: any) => any; // 非泛型函数 let fn2: <T>(arg: T) => T;
-
相比非泛型函数,泛型函数可以在使用时统一函数中的数据类型,而
any
不具有统一性 -
泛型接口与泛型函数类似:
interface I { <T>(x: T): T; }
(4)泛型类
-
泛型类的定义方法与泛型函数类似
-
举例:模拟数组
class MyArray<T> { private data: T[]; private count: number; private capacity: number; constructor(capacity: number) { this.data = new Array(capacity); this.count = 0; this.capacity = capacity; } push(element: T): void { if (this.count === this.capacity) { this.resize(this.capacity * 2); } this.data[this.count] = element; this.count++; return; } pop(): T { if (this.count === 0) { throw new Error("Empty array"); } const element = this.data[this.count - 1]; this.count--; if (this.count < this.capacity / 4) { this.resize(this.capacity / 2); } return element; } private resize(newCapacity: number): void { const newData = new Array(newCapacity); for (let i = 0; i < this.count; i++) { newData[i] = this.data[i]; } this.data = newData; this.capacity = newCapacity; } } let arr = new MyArray<number>(10); arr.push(1); arr.push(2); arr.push(3); console.log(arr); arr.pop(); console.log(arr);
(5)泛型约束
-
通过接口与
extends
关键字实现约束 -
用于确保它至少具有某些特定的属性或符合某个特定的接口
-
举例:
interface Length { length: number; } function fn<T extends Length>(arg: T): T { console.log(arg.length); return arg; }
-End-