TypeScript - 基本类型
# 1. 类型声明
# 类型声明的作用
- 类型声明 是 TypeScript 的核心特性之一。它用于指定变量、参数等的类型,以确保类型安全。
- 在 TypeScript 中,你可以通过类型声明为变量设置期望的类型,这样 TypeScript 编译器会在编译时自动检查值是否符合类型声明,确保代码的类型一致性。如果值不符合声明的类型,编译器会报错。
# 类型声明的语法
变量声明:
let 变量: 类型; // 仅声明类型 let 变量: 类型 = 值; // 声明类型并初始化值
1
2函数声明:
function fn(参数1: 类型1, 参数2: 类型2): 返回类型 { // 函数体 }
1
2
3
# 自动类型判断
TypeScript 具有自动类型判断功能。当你在声明变量的同时进行赋值时,TypeScript 编译器会自动推断变量的类型。此时,你可以省略显式的类型声明。
let 数字变量 = 10; // TypeScript 自动推断类型为 number
1
# 2. 类型
类型 | 例子 | 描述 |
---|---|---|
number | 1, -33, 2.5 | 任意数字 |
string | 'hi', "hi", hi | 任意字符串 |
boolean | true、false | 布尔值 true 或 false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的 any |
void | 空值(undefined) | 没有值(或 undefined) |
never | 没有值 | 不能是任何值 |
object | {name:'孙悟空'} | 任意的 JS 对象 |
array | [1,2,3] | 任意 JS 数组 |
tuple | [4,5] | 元素,TS 新增类型,固定长度数组 |
enum | enum{A, B} | 枚举,TS 中新增类型 |
# 数字(number)
在 TypeScript 中,所有的数字都是浮点数。它们的类型是
number
。除了支持十进制和十六进制字面量外,TypeScript 还支持 ECMAScript 2015 中引入的二进制和八进制字面量。let decimal: number = 6; // 十进制数字 let hex: number = 0xf00d; // 十六进制数字 let binary: number = 0b1010; // 二进制数字 let octal: number = 0o744; // 八进制数字 let big: bigint = 100n; // 大整数(BigInt)
1
2
3
4
5
# 布尔值(boolean)
boolean
类型表示 true 或 false。是最基本的数据类型之一。let isDone: boolean = false; // 布尔值
1
# 字符串(string)
string
类型用于表示文本数据。可以使用单引号('
)、双引号("
)或反引号(`
)定义字符串。let color: string = "blue"; // 双引号定义字符串 color = 'red'; // 单引号定义字符串
1
2使用反引号(模版字符串)可以创建多行文本和嵌入表达式:
let name: string = `Yee`; // 模版字符串定义 let age: number = 37; let sentence: string = `Hello, my name is ${name}. I'll be ${age + 1} years old next month.`; // 嵌入表达式
1
2
3
4
5上面的模版字符串等价于:
let sentence: string = 'Hello, my name is ' + name + '.\n\n' + 'I\'ll be ' + (age + 1) + ' years old next month.';
1
2
# 字面量(Literal Types)
字面量类型可以限制变量的值为特定的字面量值,通常用于定义枚举值或常量值。你也可以使用联合类型来连接多个字面量值。
let a1: 10; // 字面量类型,值只能是 10 a1 = 10; // 合法 // a1 = 5; // 报错:类型“5”不能赋给类型“10” let b1: "male" | "female"; // 联合类型,值可以是 "male" 或 "female" b1 = "male"; // 合法 b1 = "female"; // 合法 let c1: boolean | string; // 联合类型,值可以是 boolean 或 string c1 = true; // 合法 c1 = 'hello'; // 合法
1
2
3
4
5
6
7
8
9
10
11
# any
any
类型 允许你在编程阶段不明确指定变量的类型,这在处理动态内容(如用户输入或第三方代码库)时非常有用。- 使用
any
类型时,TypeScript 会跳过类型检查,允许你将任意类型的值赋给标记为any
的变量。这使得any
类型在某种程度上等同于 JavaScript 中的自由类型。 - 然而,不建议过度使用
any
类型,因为它会降低 TypeScript 的类型安全性,使得类型检查功能失效。
示例:
let d: any; // 显式地将变量 d 声明为 any 类型
// `any` 类型的变量可以接受任何类型的值
d = 10; // 数字
d = 'hello'; // 字符串
d = true; // 布尔值
// 如果没有显式指定类型,TypeScript 会默认为 any 类型
let e; // 隐式地将变量 e 推断为 any 类型
e = 5;
e = 'world';
e = false;
2
3
4
5
6
7
8
9
10
11
12
# unknown
unknown
类型 也是一种类型不确定的类型,但它比any
类型更安全。unknown
表示未知的类型,类似于any
,但更严格,避免了不必要的类型错误。- 与
any
不同,unknown
类型的值不能直接赋值给其他变量,必须经过类型检查或类型断言才能进行赋值操作。
示例:
let notSure: unknown = 4; // 使用 unknown 类型声明变量
notSure = 'hello'; // 可以赋值为字符串
notSure = true; // 也可以赋值为布尔值
// 使用 unknown 类型的变量不能直接赋值给其他变量
let s: string = "s";
// s = notSure; // 报错,因为 unknown 类型不能直接赋值给 string 类型
// 解决方法:
if (typeof notSure === "string") {
s = notSure; // 经过类型检查后,可以安全地赋值
}
// 类型断言
s = notSure as string; // 类型断言,告诉 TypeScript 变量 notSure 是 string 类型
// 或者
s = <string>notSure; // 另一种类型断言的语法
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# void
void
类型 用于表示没有值,通常用于函数的返回类型,表示该函数不返回任何值。void
类型类似于 JavaScript 中的undefined
,但在 TypeScript 中,void
类型用来标记函数不返回值的情况。
示例:
function testVoid(): void {
console.log('This function returns void');
// 函数没有返回值,类型为 void
}
2
3
4
# never
never
类型 代表那些永不存在的值。例如,never
类型通常用于那些总是抛出异常或者没有返回值的函数。never
类型是所有类型的子类型,任何类型都可以赋值给never
,但没有类型是never
的子类型,甚至any
也不能赋值给never
。
示例:
// 返回 never 的函数总是抛出异常或进入无限循环
function error(message: string): never {
throw new Error(message); // 抛出异常,函数永不会正常结束
}
function fail() {
return error("Something failed"); // 调用 error 函数,返回 never 类型
}
function infiniteLoop(): never {
while (true) {
// 无限循环,函数永不会正常结束
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# null 和 undefined
null
和undefined
类型 用于表示空值。null
表示缺少对象,而undefined
表示变量未初始化或缺少值。- 默认情况下,
null
和undefined
是所有类型的子类型,因此可以将它们赋值给其他类型的变量。 - 使用
--strictNullChecks
标记时,null
和undefined
只能赋值给void
或其本身,这样可以避免很多常见的错误。可以使用联合类型来明确一个值可以是某种类型或null
或undefined
。
示例:
let u: undefined = undefined; // 声明 undefined 类型的变量
let n: null = null; // 声明 null 类型的变量
// 默认情况下,null 和 undefined 可以赋值给其他类型
let num: number = null; // 合法,默认情况下允许
// 开启严格空检查后,null 和 undefined 只能赋值给 void 和自身
let strictNum: number = null; // 报错,必须使用联合类型来允许 null
let strictNumOrNull: number | null = null; // 合法,使用联合类型
2
3
4
5
6
7
8
9
# object
object
类型 表示所有非原始类型,也就是除了number
、string
、boolean
、symbol
、null
和undefined
之外的所有类型。- 使用
object
类型可以用来标记一个值是非原始类型,常用于表示通过Object.create
等 API 创建的对象。
示例:
let obj: object = {}; // obj 的类型是 object,表示它是一个对象
- 在实际开发中,我们通常不会直接使用
object
类型来表示对象,而是使用更具体的类型来描述对象的结构,例如使用接口或类型别名来定义对象的属性和类型。
示例:
// 定义一个具有 name 属性的对象类型
let b: { name: string };
b = { name: '孙悟空' }; // 合法,b 的属性 name 是 string 类型
// 使用交叉类型 & 来组合多个类型
let j: { name: string } & { age: number };
j = { name: '孙悟空', age: 18 }; // 合法,j 既有 name 属性也有 age 属性
// 如果对象类型中指定了多个属性,则赋值时必须提供所有属性
let b: { name: string, age: number };
b = { name: '孙悟空' }; // 报错,因为缺少 age 属性
// 使用可选属性 ? 来允许属性缺失
let b: { name: string, age?: number };
b = { name: '孙悟空' }; // 合法,因为 age 属性是可选的
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 动态属性:可以通过索引签名来定义对象可以具有的任意属性类型。
示例:
// 定义一个对象,它的属性名是字符串,属性值是任意类型
let c: { name: string, [propName: string]: any };
c = { name: "猪八戒", age: 18, gender: '男' };
// 使用数字索引签名来定义数组类型的属性
let d: { name: string, [propName: number]: any };
d = { name: "猪八戒", 0: 18 }; // 合法,0 是数字索引
// d = { name: "猪八戒", "age": 18 }; // 报错,因为属性 age 是字符串类型
2
3
4
5
6
7
8
# 数组
- 数组 可以通过两种方式在 TypeScript 中定义:
- 使用类型后缀
[]
:表示数组中所有元素的类型。 - 使用数组泛型
Array<元素类型>
:提供了另一种定义数组的方式。
- 使用类型后缀
示例:
// 使用类型后缀定义数组
let list: number[] = [1, 2, 3]; // list 是一个数字数组
// 使用数组泛型定义数组
let list: Array<number> = [1, 2, 3]; // list 是一个数字数组
2
3
4
5
# 元组 Tuple
- 元组 类型表示一个已知元素数量和类型的数组,元素的类型可以不同。元组在 TypeScript 中用来定义具有固定数量和类型的元素的数组。
示例:
// 定义一个元组,包含一个字符串和一个数字
let x: [string, number];
x = ['hello', 10]; // 合法,元素顺序和类型都正确
x = [10, 'hello']; // 报错,元素顺序和类型不匹配
// 访问元组中的元素,获取正确的类型
console.log(x[0].substr(1)); // 合法,x[0] 是字符串,字符串有 substr 方法
console.log(x[1].substr(1)); // 报错,x[1] 是数字,没有 substr 方法
// 访问越界的元素
x[3] = 'world'; // 合法,字符串可以赋值给 (string | number) 类型
console.log(x[5].toString()); // 合法,'string' 和 'number' 类型都有 toString 方法
x[6] = true; // 报错,布尔类型不是 (string | number) 类型
2
3
4
5
6
7
8
9
10
11
12
13
- 注意:自 TypeScript 3.1 版本之后,访问越界的元组元素会导致编译错误,推荐避免这种做法。
# 枚举
enum
类型 是 TypeScript 提供的一种用于定义一组具有命名值的常量的类型。它可以将一组相关的数值赋予友好的名字,类似于其他编程语言中的枚举。
示例:
// 定义一个枚举类型 Color,包含 Red, Green 和 Blue 三个成员
enum Color { Red, Green, Blue }
let c: Color = Color.Green; // c 的值是 Color.Green,对应的数值是 1
// 可以手动设置枚举成员的值
enum Color { Red = 1, Green, Blue } // Red 的值是 1,Green 的值是 2,Blue 的值是 3
let c: Color = Color.Green; // c 的值是 Color.Green,对应的数值是 2
// 全部成员手动赋值
enum Color { Red = 1, Green = 2, Blue = 4 }
let c: Color = Color.Green; // c 的值是 Color.Green,对应的数值是 2
// 根据数值获取枚举的名字
let colorName: string = Color[2]; // colorName 的值是 'Green'
console.log(colorName); // 输出 'Green'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3. 类型断言
有时在开发中,你可能比 TypeScript 更清楚一个值的具体类型。类型断言允许你通过告诉 TypeScript 编译器该值的具体类型来获得更精确的控制。类型断言相当于其他语言中的类型转换,但不进行特殊的数据检查或解构。
类型断言不会在运行时产生影响,仅在编译阶段生效。它告知 TypeScript 编译器相信你的判断,假定你已经进行了必要的类型检查。
# 类型断言的语法
类型断言有两种语法形式:
尖括号语法
let someValue: unknown = "this is a string"; // 使用尖括号语法进行类型断言 let strLength: number = (<string>someValue).length;
1
2
3
4- 这里
someValue
的类型是unknown
,但我们知道它实际上是一个字符串。 - 通过
<string>
进行类型断言,将someValue
转换为字符串类型,然后使用.length
属性获取字符串长度。
- 这里
as
语法let someValue: unknown = "this is a string"; // 使用 as 语法进行类型断言 let strLength: number = (someValue as string).length;
1
2
3
4as
语法是另一种类型断言的方式,与尖括号语法功能相同。- 这种语法在使用 JSX 时是唯一允许的类型断言方式,因为 JSX 本身使用尖括号。
# 使用场景
类型断言适用于当你比编译器更了解类型信息的情况。
例如,在操作 DOM 时,你可以断言一个
HTMLElement
的类型,以便访问特定的属性或方法:// 假设获取的元素是一个 HTMLInputElement let inputElement = document.getElementById("inputId") as HTMLInputElement; inputElement.value = "Hello, TypeScript!";
1
2
3- 在这里,我们通过
as HTMLInputElement
断言元素类型为HTMLInputElement
,以便访问value
属性。
- 在这里,我们通过
# 4. 类型别名
类型别名是通过 type
关键字为类型定义一个新的名称。类型别名用于简化复杂类型的使用,特别是在涉及多个联合类型或其他复杂类型时。
# 类型别名的定义
通过类型别名,可以为复杂类型指定一个简洁的名称,便于在代码中重用。
// 定义一个类型别名 t,用于表示字符串或布尔值
type t = string | boolean;
// 使用类型别名为变量 a 和 b 声明类型
let a: t = "字符串"; // a 可以是字符串
let b: t = true; // b 可以是布尔值
// 与直接使用联合类型的效果相同
let a: string | boolean = "字符串";
let b: string | boolean = true;
2
3
4
5
6
7
8
9
10
# 类型别名的优势
- 简化代码:类型别名可以减少代码冗余,简化对复杂类型的引用。
- 增加可读性:通过为复杂类型提供有意义的名称,可以提高代码的可读性和可维护性。
- 灵活性:类型别名不仅可以用于基本类型,还可以用于联合类型、交叉类型、对象类型等。
示例:
// 定义一个复杂类型的别名
type Point = {
x: number;
y: number;
};
// 使用类型别名为对象定义类型
let point: Point = {
x: 10,
y: 20
};
// 定义联合类型的别名
type StringOrNumber = string | number;
// 使用类型别名为变量定义类型
let data: StringOrNumber = "Hello";
data = 123;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18