之前我们讲解了TypeScript的安装过程,接下来,就让我们开始正式的学习TypeScript的基础类型
TypeScript的基础类型
我们前面说过,TypeScript是JavaScript的超集,那么TypeScript肯定是包含了我们JavaScript的基础类型,但是又不限于我们JavaScript的基础类型,TypeScript在 字符串(string)
、数字型(number)
、数组(array)
、布尔型(boolean)
、空(Null)
、未定义(Undefined)
、对象(Object)
类型的基础上。
还新增了枚举(enum)
、元组(tuple)
、Any 类型
、Unknown 类型
、Void 类型
、 Never 类型
、可选类型
Boolean、Number、String、Array、boolean类型
// Boolean 类型
let Done: boolean = false;
// ES5:var Done = false;
// Number 类型
let count: number = 10;
// ES5:var count = 10;
//String 类型
let name: string = "Semliker";
// ES5:var name = 'Semlinker';
//Array 类型
let list: number[] = [1, 2, 3];
// ES5:var list = [1,2,3];
let list: Array<number> = [1, 2, 3]; // Array<number>泛型语法
// ES5:var list = [1,2,3];
Object类型
object对象类型可以用于描述一个对象
//创建对象
let person: object = {
name: "zayyo",
age: 25,
isStudent: true
};
console.log(person); // 输出:{ name: 'zayyo', age: 25, isStudent: true }
interface(接口)
interface(接口)可以帮助我们定义对象的结构和类型。通过接口,我们可以指定对象应该包含哪些属性、方法以及它们的类型。帮助我们更好的进行代码的复用和优化
使用interface(接口) 来指定对象的类型
interface Person {
name: string;
age: number;
isStudent: boolean;
}
let person: Person = {
name: "zayyo",
age: 25,
isStudent: true
};
console.log(person.name); // 输出:"zayyo"
?可选类型
我们的对象类型也可以指定哪些属性是可选的(可有可无),我们可以在属性的后面添加一个?:
//声明可选属性
interface Person {
name: string;
age?: number; // age 是可选属性
}
let person: Person = {
name: "Alice" }; // age 未定义
//可选类型结合可选链操作,使用可选链操作符访问属性
let person: Person | null = getPersonFromDatabase();
let age = person?.age;
Array 类型
数组类型可以帮助我们在单个变量中存储多个值。更方便地对这些值进行处理
let list: number[] = [1, 2, 3];
// ES5:var list = [1,2,3];
Null 和 Undefined 类型
- null 类型表示一个空引用或者值的缺失。
- undefined 类型表示某个值未经初始化或者不存在。
在JavaScript中这两个类型是不能相互赋值的,但是在 TypeScript 中,这两种类型是可以互相赋值的。具体来说:
- 当我们启用了 “strictNullChecks(严格模式)” 时(默认情况下是启用状态),我们是不能将 null 或 undefined 赋给其他非兼容类型的变量。
let myVar: string | null = null;
let myOtherVar: number | undefined = undefined;
// 可以赋给联合类型
myVar = "Hello";
myOtherVar = 10;
// null和undefined不允许直接赋给其他非兼容类型,
let someString: string = myVar; // 报错,因为变量myVa可能为null
let someNumber: number = myOtherVar; // 报错,因为变量myOtherVar可能为undefined
那如果我就是想要进行赋值,是不是就没有办法了呢?
我们可以通过条件判断进行赋值
if (myVar !== null) {
someString = myVar;
}
if (myOtherVar !== undefined) {
someNumber= myOtherVa r;
}
- 但是,在非严格模式下,我们可以将 null 或 undefined 赋给任何类型的变量
let myVar: string = null; // 不会报错
let myOtherVar: number = undefined; // 不会报错
// 可以直接赋给其他非兼容类型
let someString: string = myVar;
let someNumber: number = myOtherVar;
// 可以将 null 赋给 undefined 类型变量
myOtherVar = null;
// 可以将 undefined 赋给 null 类型变量
myVar = undefined;
Enum 枚举类型
num(枚举)类型用于定义一组命名常量。它可以帮助我们为这些常量赋予有意义的名称,帮助我们提高代码的可读性。
//创建一个颜色枚举
enum Color {
Red,
Green,
Blue
}
console.log(Color.Green); // 输出:1 (Green 的索引值)
代码解释:
- 创建一个颜色枚举,并列出了三个可能的取值:Red、Green 和 Blue。
- 默认情况下,它们会依次被赋予索引值:0、1、2。(初始值为 0,其余的成员会从 1 开始自动增长)
- 我们直接打印对应的颜色,就会得到他们对应的索引
当然我们也可以设置 Color 的初始值,当然我们不仅可以赋值为Number也可以赋值为字符串类型。
但是我们要注意的是数字枚举相对字符串枚举多了 “反向映射”
enum Color {
White,
Red = 3,
Green = 2,
Blue = "zayyo",
}
console.log(Color.Green); // 输出:2 (Green 的索引值)
console.log(Color[3]); // 输出:Red
console.log(Color[0]); // 输出:White
你可能会问Enum(枚举类型)到底有什么用途呢?接下来我们就看一个Enum(枚举类型)的妙用.
我们创建了一个函数来检查给定颜色是否属于 Color 枚举
function isColorValid(color: Color): boolean {
return color === Color.Red || color === Color.Green || color === Color.Blue||color === Color.White;
}
console.log(isColorValid(Color.Red)); // 输出:true
console.log(isColorValid(Color.Yellow)); // 输出:false
Any 类型
在某些情况下,我们确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候我们就可以使用any类型。 在 TypeScript 中,任何类型都可以被归为 any 类型。这就让 any 类型成为了类型系统的顶级类型(也被称作全局超级类型)。
any类型有点像一种讨巧的TypeScript手段:我们可以对any类型的变量进行任何的操作,包括获取不存在的属性、方法。也可以给一个any类型的变量赋值任何的值,比如数字、字符串的值;
//定义 any 类型变量
let myVariable: any;
myVariable = "Hello"; // 可以赋予字符串类型值
myVariable = 10; // 可以赋予数字类型值
myVariable = true; // 可以赋予布尔类型值
//调用任意方法和属性
let myValue: any = "Hello";
console.log(myValue.toUpperCase()); // 输出 "HELLO"
我们可以在任何场景下,使用 any
类型,但是过度使用它可能破坏 TypeScript 的主要目标之一,静态类型安全。为了解决 any
带来的安全隐患,TypeScript 在3.0版本中 引入了 unknown
类型。
Unknown 类型
unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量,它和any类型有点类似,所有类型都可以赋值给
any
,所有类型也都可以赋值给unknown
。但是在unknown类型的值上做任何事情都是不合法的。
可能这样说让你很难理解,让我们来看代码示例吧
let myVariable: unknown;
myVariable = "Hello"; // 可以赋予字符串类型值
myVariable = 10; // 可以赋予数字类型值
myVariable = true; // 可以赋予布尔类型值
//但是将类型为 `unknown` 的值赋值给any除外的其他类型的变量是不被允许的。
let value2: boolean = myVariable; // Error
let value3: number = myVariable; // Error
let value5: string = myVariable; // Error
//同样类型为 `unknown` 的值也是不允许被执行操作的
myVariable.foo.bar; // Error
myVariable.trim(); // Error
myVariable(); // Error
new myVariable(); // Error
myVariable[0][1]; // Error
但是如果我们不得不对类型为Unknown
的值进行方法的调用时该怎么办呢?
我们可以进行类型检查和断言后,再进行调用。
function processValue(value: unknown) {
if (typeof value === 'string') {
console.log(value.toUpperCase()); // 通过 typeof 检查后可以直接调用字符串方法
} else if (typeof value === 'number') {
console.log(Math.abs(value)); // 通过 typeof 检查后可以进行数学运算
} else {
console.log("Unknown type");
}
}
processValue("Hello"); // 输出 "HELLO"
processValue(10); // 输出 10
processValue(true); // 输出 "Unknown type"
// 使用 as 关键字进行断言
let strLength: number = (myVariable as string).length;
console.log(strLength); // 如果 myVariable 是字符串,则输出其长度
Tuple 元组类型
当我们需要将不同类型的元素存储在一个数组中时,就可以使用Tuple。其工作方式类似于数组,但是元组中每个元素都有自己特性的类型,我们根据索引值获取到的值可以确定对应的类型。而数组存放相同类型的元素。
//## 声明和初始化元组
let person: [string, number, boolean];
person = ["Alice", 30, true];
//访问元祖元素
let name: string = person[0];
let age: number = person[1];
let isActive: boolean = person[2];
//展开运算符可以将一个元组中的元素添加到另一个元组中:
let moreInfo: [string, string] = ["New York", "USA"];
let extendedPerson: [...typeof person, ...typeof moreInfo] = [...person, ...moreInfo];
注意:在元组初始化的时候,我们必须提供每个属性的值,不然会出现报错
Void 类型
void 类型用于表示函数没有返回值。这意味着当您声明一个函数的返回类型为 void 时,该函数不会返回任何值。
console.log("Hello!");
}
注意:我们可以将undefined或null赋值给void类型,也就是函数可以返回undefined或 null
Never 类型
never 类型表示那些永远不会发生的值。它通常用于表示无法正常结束的函数或表达式,如抛出异常或进入无限循环。
当一个函数抛出异常,它就不会返回正常的结果,因此返回类型应为 never。
例如:
function throwError(message: string): never {
throw new Error(message);
}
//函数 throwError 永远不会返回一个值,因为它总是抛出一个异常,导致代码无法继续执行。
当一个函数陷入无限循环,它也不会返回值,因此返回类型同样是 never。
例如:
function infiniteLoop(): never {
while (true) {
// 无限循环
}
}
联合类型
当我们代码中的变量可能是几个的类型时,我们就可以使用联合类型,联合类型允许一个变量拥有多个可能的类型。这意味着我们可以在一个变量上使用多个不同的类型注解,用于表示它可以是这些类型中的任何一个。用竖线符号(|)将这些类型分隔开
例如:
let myVar: string | number;
myVar = "Hello, TypeScript!"; // 合法
myVar = 42; // 合法
可辨识联合
可辨识联合(Discriminated Unions),也被称为标签联合或代数数据类型,可辨识联合允许你创建具有共同字段(标签)的不同变体,并使用这些标签来区分和识别不同的联合类型成员。这种类型的本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。
interface Square {
kind: "square";
size: number;
}
interface Circle {
kind: "circle";
radius: number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
type Shape = Square | Circle | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size ** 2;
case "circle":
return Math.PI * shape.radius ** 2;
case "triangle":
return (shape.base * shape.height) / 2;
default:
// TypeScript会检查是否已处理所有可能的标签值
throw new Error("Invalid shape");
}
}
类型守卫
传入给一个联合类型的值给函数是非常简单的:只要保证是联合类型中的某一个类型的值即可,但是我们拿到这个值之后,我们应该如何使用它呢?
因为它可能是任何一种类型。比如我们拿到的值可能是string或者number,我们就不能对其调用string上的一些方法。那么我们怎么处理这样的问题呢?
这时我们就可以使用我们的类型守卫,去解决
function displayType(value: string | number) {
if (typeof value === "string") {
console.log(`Type is string: ${
value.length}`);
} else {
console.log(`Type is number: ${
value.toFixed(2)}`);
}
}
displayType("Hello, TypeScript!"); // Type is string: 19
displayType(42); // Type is number: 42.00
交叉类型
当我们需要将多个类型合并为一个类型时,就可以使用交叉类型。交叉类型允许我们将多个类型合并成一个新的类型。我们需要使用符号 & 来创建交叉类型。交叉类型会将包含所有类型的属性,合并在一起。
type Person = {
name: string;
age: number;
};
type Employee = {
jobTitle: string;
salary: number;
};
type EmployeePerson = Person & Employee;
const employeePerson: EmployeePerson = {
name: "Bob",
age: 25,
jobTitle: "Designer",
salary: 60000,
}
console.log(employeePerson)
//输出{ name: "Bob", age: 25, jobTitle: "Designer", salary: 60000,}