web作业-TypeScript总结
TypeScript(ts)
可以理解为 JavaScript
的超集。TypeScript
本身不能直接运行, 因为 JavaScript
引擎并不会认识 TypeScript
, 所以他需要先编译成 JavaScript
代码, 然后才能运行。
数据类型
基本的数据类型与 js
一致,下面则几种则是比较特殊的:
enum
enum 即枚举类型1
2
3
4
5
6
7enum Shading {
Flat, // 0
Gouraud, // 1
Phong, // 2
}
let shadingType: Shading = Shading.Phong; // shadingType = 2
如上所示,枚举实际也是个 number
值,其每个成员的值从上往下依次递增,如果没有明确指定首个成员的值,那么默认为0.
此外还有两种特别的枚举:字符串枚举和异构枚举
字符串枚举
在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。1
2
3
4
5
6
7enum Shading {
Flat = "Flat",
Gouraud = "Gouraud",
Phong = "Phong",
}
let shadingType: Shading = Shading.Flat;
异构枚举
异构枚举的成员值是数字和字符串的混合1
2
3
4
5enum EnumExample {
A = 1,
S_A = "str",
B = 2,
}
Any
在 ts
中,任何类型都可以被归为 Any
类型因此其也叫全局超级类型,可以把 typescript
写成 anyscript
了1
2
3let anyvar: any = "123";
anyvar = 5;
anyvar = true;
any 类型本质上是类型系统的一个逃逸舱。方便了开发者的同时也给程序带来了很多麻烦,如果滥用 any
的话那就真的成 anyscript
。因此,后面引入了 unknown
unknown
和 any
一样的是,所有类型的值也可以赋给 unknown
,因此 unknown
也是 ts
中的一种顶级类型,例如:1
2
3
4
5
6
7
8let value: unknown;
value = 5;
value = {};
value = "564";
value = [];
value = null;
value = undefined;
可以看出对 unknown
的所有赋值操作都是没问题的,可是我们再看看使用 unknown
类型给其他类型赋值会怎么样1
2
3
4let value: unknown;
value = 6;
let value_number: number = value;
// error TS2322: Type 'unknown' is not assignable to type 'number'.
可以看到,无法将 number
类型分配给 number
类型,即使里面存储的是 number
类型。unknown 类型只能被赋值给 any
类型和 unknown
类型本身。
tuple
tuple
即元祖,其可以存储各种不同的类型。元组可用于定义具有有限数量的未命名属性的类型。每个属性都有一个关联的类型。使用元组时,必须提供每个属性的值。
使用过其他语言的元祖时就很好理解了,例如 c++
中使用 tuple
返回多个类型的值:1
2
3std::tuple<std::string, int, float> return_tuple() {
return {"123", 5, 0.1f};
}
同样在 ts
中也可以使用 tuple
写出相应的函数:1
2
3function return_tuple(): [string, number] {
return ["123", 123];
}
由此可知几下这点:
tuple
的声明类似于数组:1
const tuple_value: [string, number] = ["456", 123];
tuple
声明类型后初始化时如果类型不同不能交换两个值的位置,也不能有空缺1
2const tuple_value: [string, number] = [123, "456"]; // error
const tuple_value: [string, number] = [123]; // error
void
void
类型表示它没有任何类型,当一个函数没有返回值时,就可以将其返回类型声明为 void
1
2
3function return_void(): void {
console.log("in function return_void");
}
值得注意的是:声明一个 void
类型的变量并没用什么意义,因为其值只能为 undefined
或者 null
1
let void_value: void = null;
组合类型
ts
支持一个变量可以被赋予多种不同的类型,多个类型之间使用 |
号分割1
2
3
4let value: number | string;
value = 999; // ok
value = "123"; // ok
value = true; // error
never
never 类型表示不存在的类型,即表示永远不会发生的情况或不应该发生的情况。它通常用于表示错误或不可能到达的代码分支。1
2
3function throwError(message: string): never {
throw new Error(message);
}
还有一种使用方式就是配合 switch
语句去处理无法到达的分支1
2
3
4
5
6
7
8
9
10
11function print_value(value: string | number) {
switch (typeof value) {
case "string":
return `it's a string, value: ${value}`;
case "number":
return `it's a number, value: ${value}`;
default:
const _exhaustiveCheck: never = value;
return _exhaustiveCheck;
}
}
函数
声明
1 | function functionName(agrs): retunrn_type { |
1 | let functionName = funciton(args): retunrn_type { |
必选参数和可选参数
必选参数:顾名思义调用函数时必须参数的参数。且实参和形参的类型数量都得一致1
2
3function return_info_str(name: string, age: number): string {
return `name: ${name}; age: ${age}`;
}
可选参数:顾名思义可传可不传1
2
3function return_info_str(name: string, age?: number): string {
return `name: ${name}; age: ${age}`;
}
变长参数
可以接受不定长的参数,下面是一个使用变长参数实现 sum
函数的例子:1
2
3
4
5
6
7
8
9function sum(value: number, ...values: number[]): number {
let sum = value;
for (let x of values) {
sum += x;
}
return sum;
}
console.log(sum(1, 3, 4, 88)); // output 96
此处有一个必选参数是防止接收空参数的情况
class
诸如声明,构造函数之类的,和 js
是完全差不多的,只有在函数参数和返回值标明了一点类型,下面介绍几个比较特别的特性:
抽象类
抽象类指不能被实例化的类,是提供给其他类继承的
使用 abstract
关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(也就是其子类)中实现,abstract抽象方法只能放在抽象类里面。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22abstract class Animal {
name: string
constructor(name: string) {
this.name = name;
}
abstract eat(): void;
}
class Dog extends Animal{
constructor(name: string) {
super(name);
}
eat(): void {
console.log(`Dog ${this.name} is eating`);
}
}
let dog: Dog = new Dog("hhh");
dog.eat(); // Dog hhh is eating
多态
此处指的是动态多态,不是重载函数之类的静态多态(突然不知道 c++
里面的这个概念 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
38abstract class Animal {
name: string
constructor(name: string) {
this.name = name;
}
abstract eat(): void;
}
class Dog extends Animal{
constructor(name: string) {
super(name);
}
eat(): void {
console.log(`Dog ${this.name} is eating`);
}
}
class Cat extends Animal{
constructor(name: string) {
super(name);
}
eat(): void {
console.log(`Cat ${this.name} is eating`);
}
}
function animal_eat(animal: Animal): void {
animal.eat();
}
let dog: Dog = new Dog("ddd");
let cat: Cat = new Cat("neko");
animal_eat(dog); // Dog ddd is eating
animal_eat(cat); // Cat neko is eating
接口
接口( Interfaces
)是一种用于描述对象形状( Shape
)的结构化类型。接口定义了一个对象应该包含哪些属性和方法,以及它们的类型和参数类型等信息。
属性类型接口
1 | interface Person { |
函数类型接口
我们定义了一个接口 Calculator
,它描述了一个函数类型,该函数接受两个数字类型的参数 a
和 b
,并返回一个数字类型的值。然后,我们定义了两个函数 add
和 subtract
,它们都满足 Calculator
接口的要求1
2
3
4
5
6interface Calculator {
(a: number, b: number): number;
}
const add: Calculator = (a, b) => a + b;
const subtract: Calculator = (a, b) => a - b;
泛型
泛型就是解决类、接口、方法的复用性、以及对不特定数据类型的支持。
泛型类
以下是一个简单使用泛型类的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
size(): number {
return this.items.length;
}
}
定义了一个泛型类 Stack
,它包含一个类型参数 T
。类中的 items
属性是一个类型为 T
的数组,它存储了栈中的元素。类中的 push
方法接受一个类型为 T 的参数 item
,并将它添加到栈中。类中的 pop
方法弹出栈顶元素,并返回一个类型为 T | undefined
的值。类中的 size
方法返回当前栈的大小。
以下是一些使用的例子:1
2
3
4
5
6
7
8
9
10
11const stack1 = new Stack<number>();
stack1.push(1);
stack1.push(2);
console.log(stack1.pop()); // output 2
console.log(stack1.size()); // output 1
const stack2 = new Stack<string>();
stack2.push("hello");
stack2.push("world");
console.log(stack2.pop()); // output "world"
console.log(stack2.size()); // output 1
泛型接口
1 | interface Map<T> { |
定义了一个泛型接口 Map
,它包含一个类型参数 T
。接口中的索引签名 [key: string]: T
表示该接口可以用字符串类型的键索引任意类型的值