3. TypeScript 中的数据类型
在 TypeScript 中,类型(Types)允许你定义并强制执行应用中数据的结构。通过使用类型,你可以在编译阶段捕捉错误,而不是等到运行时才发现,从而让代码更加可预测,也更不容易出现 bug。TypeScript 提供了丰富的类型系统,可以处理从简单的值到复杂对象等各种数据结构。
一、数据类型
在 TypeScript 中,数据类型定义了变量可以保存的值的种类,从而确保类型安全并提升代码的清晰度。
- 原始类型(Primitive Types):基本类型,如
number
、string
、boolean
、null
、undefined
和symbol
。 - 对象类型(Object Types):复杂结构,包括数组(arrays)、类(classes)、接口(interfaces)和函数(functions)。
(一) 原始类型(Primitive Types)
原始类型是 TypeScript 中最基本的数据类型。它们表示简单的、不可变的值,通常通过直接赋值进行使用。
类型 | 关键字 | 描述 |
Number |
| 表示整数和浮点数。 |
String |
| 表示文本数据。 |
Boolean |
| 表示逻辑值: |
Null |
| 表示有意的空值。 |
Undefined |
| 表示尚未初始化的变量。 |
Symbol |
| 表示唯一且不可变的值,常用作对象的键。 |
BigInt |
| 表示任意精度的整数。 |
(二) 对象类型(Object Types)
对象类型是更复杂的结构,可以包含多个值和函数。它们是可变的,创建后可以被修改。
类型 | 描述 |
Object | 表示任何非原始类型;但建议使用更具体的类型替代该类型。 |
Array | 表示特定类型元素的集合。 |
Tuple | 表示具有固定数量和类型的元素数组。 |
Enum | 表示一组命名常量,用于表示相关联的值集合。 |
Function | 表示可调用的实体;可以定义参数和返回值类型。 |
Class | 定义用于创建具有特定属性和方法的对象的蓝图。 |
Interface | 描述对象的形状,指定属性名称及其类型。 |
(三) 高级类型(Advanced Types)
TypeScript 还提供了一些高级类型,以便进行更复杂的类型定义。
类型 | 描述 |
Union | 允许变量持有多种类型之一,为类型赋值提供灵活性。 |
Intersection | 将多个类型合并为一个,要求值满足所有包含的类型。 |
Literal | 限定变量只能被赋予某个特定的确切值。 |
Mapped | 通过指定规则对已有类型的属性进行转换,从而创建新类型。 |
(四) TypeScript 中使用数据类型的最佳实践
- 使用
let
和const
替代var
:使用let
和const
进行块级作用域的变量声明,以避免变量提升和作用域泄露的问题。 - 避免使用
any
类型:避免使用any
,因为它会绕过类型检查;应优先使用具体的类型以保持类型安全。 - 利用类型推断:在可能的情况下让 TypeScript 自动推断类型,以减少冗余并提高代码可读性。
- 使用内置工具类型:利用如
Partial<T>
和Readonly<T>
等内置工具类型,创建更灵活、可读性更高的类型定义。
二、原始类型
在本文中,我们将学习 TypeScript 中的原始类型:字符串(string)、数字(number)和布尔值(boolean)。这些是最常用和最基本的数据类型。接下来我们将详细介绍 string、number 和 boolean 的使用方法。
(一) typeScript 原始数据类型
TypeScript 原始数据类型与 JavaScript 常用的数据类型类似,TypeScript 主要有三种原始数据类型(Primitives):
1. String(字符串)
TypeScript 中的字符串与句子类似。它们由一系列字符组成,本质上就是一个“字符数组”,例如 "Hello TypeScript"
等。
2. Number(数字)
与 JavaScript 一样,TypeScript 支持 number
数据类型。所有数字在底层都以浮点数形式存储,无论是整数还是小数。
3. Boolean(布尔值)
最基本的数据类型是简单的 true
或 false
值,这种类型在 JavaScript 和 TypeScript 中都称为布尔类型(boolean)。
(二) 字符串类型
字符串(string)数据类型表示文本或字符序列。在 JavaScript 和 TypeScript 中,字符串用单引号或双引号括起来。
1. 语法
let 变量名: string = "值";
说明:
- 变量名(variableName):这是变量的名称。
- string:这是类型注解,指定该变量应该存储一个字符串。
- "值"("value"):这是赋给变量的初始值。
2. 示例
在这个示例中,我们将学习字符串的用法。
let greeting: string = "Hello TypeScript";
console.log(greeting);
输出结果:
(三) 数字类型
数字类型表示数值,可以是整数也可以是浮点数。
1. 语法
let 变量名: number = 数值;
2. 说明
- 变量名(variableName):这是变量的名称。
- number:这是类型注解,指定该变量应存储一个数值(整数或浮点数)。
- 数值(numericValue):这是赋给变量的初始数值。
3. 示例
在这个示例中,我们将学习数字类型的用法。
let num: number = 30;
let float: number = 19.99;
console.log(num);
console.log(float);
输出结果:
(四) 布尔类型
布尔类型表示二进制的值,只能是 true
(真)或 false
(假)。它通常用于条件判断和逻辑运算。
1. 语法
let 变量名: boolean = true | false;
2. 说明
- 变量名(variableName):这是变量的名称。
- boolean:这是类型注解,指定该变量应存储一个布尔值(
true
或false
)。 - true 或 false:这是赋给变量的初始布尔值。
3. 示例
在这个示例中,我们将学习布尔类型的用法。
let isStudent: boolean = true;
let hasPermission: boolean = false;
console.log(isStudent);
console.log(hasPermission);
输出结果:
三、对象类型
TypeScript 对象是一组键值对的集合,其中键是字符串,值可以是任意数据类型。TypeScript 中的对象可以存储各种类型的数据,包括原始类型、数组和函数,从而提供一种结构化的方式来组织和操作数据。
(一) 什么是 TypeScript 对象?
在 TypeScript 中,对象是包含一组键值对的实例。这些键值对可以包含各种数据类型,包括标量值(如字符串、数字等)、函数,甚至是其他对象的数组。
TypeScript 中的“对象”指的是任何非原始值(即不是 string、number、bigint、boolean、symbol、null 或 undefined)。这与空对象类型 {}
不同,也与全局类型 Object
不同。
1. TypeScript 对象类型(TypeScript Objects types)
在 TypeScript 中,对象类型可以通过 接口(interface)、类型别名(type alias) 或 索引签名(index signature) 来定义。
接口和类型别名用于为对象指定结构,确保其符合特定的形状;而索引签名则允许对象具有动态的属性名,并为这些属性指定统一的值类型。
2. 语法
let 对象名称 = { 属性名: 值,
}
3. 参数说明
- 对象名称(Name_of_object):这是对象的名称。
- 属性名(object_property):这是对象的属性或键(Key)。
- 值(value):这是该属性对应的值。
(二) 对象字面量表示法(Object Literal Notation)
在 TypeScript 中,对象字面量表示法是指使用花括号 {}
直接创建包含键值对的对象。这种方式可以通过显式定义对象属性及其类型来确保类型安全。
1. 示例
在这个示例中,我们定义了一个名为 person
的对象,它包含 firstName
、lastName
和 age
属性。
const person = {firstName: "Felix",lastName: "Lu",age: 30,
};console.log(`Hello, ${person.firstName} ${person.lastName}!`);
输出:
Hello, Felix Lu!
在上述示例中,person
是一个具有 firstName
、lastName
和 age
属性的对象。我们通过点号(dot notation)访问这些属性。
(三) 定义对象类型(Defining Object Types)
在 TypeScript 中,我们可以使用 接口(interface) 或 类型别名(type alias) 来表示对象类型。这些类型模板可以确保对象遵循特定结构,从而增强类型安全性和代码可维护性。
1. 使用接口(Using Interfaces)
接口用于定义对象的结构,指定属性名称和类型,确保对象符合特定形状。
示例
在这个示例中,我们定义了一个接口 MyObject
,并创建了一个对象 obj
,它符合该接口,同时动态添加了一个 location
属性,并通过调用 solve
方法输出结果。
interface MyObject {company: string;type: string;solve: () => string;
}const obj: MyObject & { [key: string]: any } = {company: 'MozeTop',type: 'unique',solve: function (this: MyObject) {return `company is ${this.company}, and it's ${this.type}`;}
};// 调用 solve 方法
console.log(obj.solve());// 动态添加一个字段
obj.location = "BeiJing";// 打印新添加的字段
console.log(obj.location);
输出:
company is MozeTop, and it's unique
BeiJing
在上面的示例中,我们创建了一个接口 MyObject
,并定义了一个常量 obj
,它的类型是 MyObject
与一个可以动态添加属性的扩展类型。我们调用了对象中的方法,并输出了动态添加的字段。
2. 使用类型别名(Using Type Aliases)
类型别名用于为特定类型创建一个自定义名称,包括对象结构。它可以清晰地定义对象属性的类型和结构,确保对象具有一致性和类型安全。
示例
在这个示例中,我们定义了一个 Product
类型,并创建了一个符合该类型的对象 laptop
,它包含 productId
、name
和 price
属性。
type Product = {productId: string;name: string;price: number;
};const laptop: Product = {productId: "No123",name: "iPhone 18",price: 8800,
};console.log(laptop);
输出:
{"productId": "No123","name": "Iphone 18","price": 8800
}
(四) 索引签名(Index Signatures)
TypeScript 中的索引签名允许对象具有动态的属性名,并为这些属性定义统一的值类型。这对于属性不固定的对象特别有用,可以实现灵活且类型安全的属性访问。
示例
在这个示例中,我们初始化了一个空的 nameAgeMap
对象,其键为字符串类型,值为数字类型。随后为 "Jack" 和 "Mark" 添加了年龄信息。
let nameAgeMap: { [index: string]: number } = {};
nameAgeMap["Jack"] = 25;
nameAgeMap["Mark"] = 30;
console.log(nameAgeMap);
输出:
{"Jack": 25,"Mark": 30
}
四、解释类型断言
在 TypeScript 中,类型断言允许开发者覆盖编译器推断出的类型,告知其某个值的具体类型。
类型断言完全是编译时的构造,不会改变代码的运行时行为。
在与 API 或返回值为 any
类型的第三方库交互时,它们特别有用。
let value: any = "This is a string";
let lengthOfString: number = (value as string).length;console.log(lengthOfString);
- 声明了一个
value
变量,其类型为any
,意味着它可以保存任何类型的值。 - 使用类型断言
(value as string)
,告知编译器该值应被视为字符串。 - 然后可以安全地访问
length
属性,因为编译器将该值识别为字符串。
输出:
16
TypeScript 中类型断言的更多示例:
(一) 对函数返回值进行类型断言
function getValue(): any {return 'Hello, TypeScript!';
}let strLength: number = (getValue() as string).length;
console.log(strLength);
getValue
函数返回一个any
类型的值。- 通过将返回值断言为字符串,可以安全地访问
length
属性。
输出:
18
(二) 对 DOM 元素进行类型断言
let element = document.querySelector('input[type="text"]');
let inputElement = element as HTMLInputElement;
console.log(inputElement.value);
document.querySelector
返回的是Element
类型,该类型没有value
属性。- 通过将
element
断言为HTMLInputElement
,告知 TypeScript 它是具体的元素类型,从而可以访问value
属性。
输出:
[输入框的值]
(三) 对联合类型使用类型断言
type Pet = {name: string;walk: () => void;
};type Fish = {name: string;swim: () => void;
};let myPet: Pet | Fish = { name: 'Goldie', swim: () => console.log('Swimming') };(myPet as Fish).swim();
myPet
的类型是Pet | Fish
,意味着它可以是其中之一。- 通过将
myPet
断言为Fish
,告知编译器该对象具有swim
方法,从而可以调用它。
输出:
Swimming
(四) TypeScript 中使用类型断言的最佳实践
- 尽量少用类型断言: 尽可能依赖 TypeScript 的类型推断。过度使用类型断言可能掩盖潜在错误,降低代码可维护性。
- 优先使用
as
语法: 使用as
语法进行类型断言,以避免在 JSX 等环境中使用尖括号语法引发的冲突。 - 避免断言为
any
: 将值断言为any
会失去 TypeScript 静态类型检查的好处。应优先定义精确的类型以保持类型安全。