ts基础
基本数据类型
// 字符串类型
const name: string = 'zhangsan'
// 数值类型
const age: number = 15
const hex: number = 0xff
// 布尔类型
const adult: boolean = false
const graduated: boolean = 0any类型
any,可以是如何类型,不推荐使用
let anyData: any = 123
anyData = 'zhangsan'
anyData = trueunknown类型
unkonwn,表示未知类型,会自动推导第一次赋值的类型
let unkonwnData: unkonwn
unkonwnData = 123
unkonwnData = 'zhangsan' // 报错: 类型“string”的参数不能赋给类型“number”的参数void类型
void,表示空类型,表示没有返回值
function print(): void {
console.log('hello world')
}never类型
never,表示永远不会有返回的类型
- 如抛出异常
- 死循环
function error(message: string): never {
throw new Error(message)
}
function infiniteLoop(): never {
while (true) {
}
}使用场景
它是所有类型的子类型,但没有任何类型是它的子类型(除了自身),主要用于:
- 永远不结束的函数:如抛错函数、无限循环函数;
- 类型收窄到不存在:结合条件判断,标记「不可能触发的分支」;
- 排除联合类型的所有可能:实现严格的类型穷尽检查。
<script setup lang="ts">
import {ref} from 'vue';
const number = ref(0);
// 字面类型
type Fruit = "apple" | "banana" | "orange";
function getFruitPrice(fruit: Fruit): number {
switch (fruit) {
case "apple":
return number.value = 5;
case "banana":
return number.value = 3;
case "orange":
return number.value = 4;
default:
const _exhaustiveCheck: never = fruit;
throw new Error(`未知水果:${_exhaustiveCheck}`);
}
}
</script>
<template>
{{ number }}
<button @click="getFruitPrice('apple')">苹果</button>
<button @click="getFruitPrice('banana')">香蕉</button>
<button @click="getFruitPrice('watermelon')">西瓜</button>
</template>对于never和void类型
- 两者都可表示空类型,但never比void更严格,never表示永远不存在的值,而void表示可以返回undefined。
- never类型不能赋值给任何类型,而void类型可以赋值给any类型。
- never类型表示一个不存在的值,比如never类型表示一个函数会永远不返回。
- void类型表示一个函数会返回undefined,或者没有返回值。
null和undefined类型
let name: string | null = null
let age: number | undefined = undefinedjavaScript 有两个基础值用于表示值不存在或未初始化的值:null 和 undefined。 TypeScript 有两个对应的同名类型。这些类型的行为取决于你是否启用了 strictNullChecks 选项。
关闭 strictNullChecks,可能是 null 或 undefined 的值仍然可以正常访问,并且值 null 和 undefined 可以分配给任何类型的属性。缺乏对这些值的检查往往是错误的主要来源;
启用 strictNullChecks 时,当值为 null 或 undefined 时,你需要在对该值使用方法或属性之前测试这些值。就像在使用可选属性之前检查 undefined 一样,我们可以使用缩小来检查可能是 null 的值:
function doSomething(x: string | null) {
if (x === null) {
// do nothing
} else {
console.log("Hello, " + x.toUpperCase());
}
}联合类型
联合类型,表示多个类型中的一个
let name: string | number = 'zhangsan'
name = 123
name = true // 错误: 类型“boolean”的参数不能赋给类型“string | number”的参数非空断言
TypeScript 还具有一种特殊的语法,可以在不进行任何显式检查的情况下从类型中删除 null 和 undefined。在任何表达式之后写 ! 实际上是一个类型断言,该值不是 null 或 undefined:
let name: string | null = null
name!.toUpperCase()
name.toUpperCase() // 错误: 类型“null”上不存在属性“toUpperCase”对象类型
创建对象类型,对象类型可以包含多个属性,属性类型可以任意,属性名可以任意
// 固定类型
type Person = {
name: string;
age: number;
sex?: string; // 可选属性
}
const person1: Person = {
name: 'zhangsan',
age: 18
}
const person2: Person = {
name: 'lisi',
age: 19,
sex: 'male'
}
const person3: Person = {
name: 'wangwu',
sex: 'female'
} // 错误: 属性“age”缺少,或者属性“age”的类型不匹配
// 泛型映射类型
// T表示键名类型,U表示键值类型
type Animal<T, U> = {
[key in T]: U;
}
// 比如给猫定义属性
const car: Animal<string, number> = {
speed: 100,
weight: 2000
}
// 统计家禽的数量
const fowl: Animal<'chicken' | 'durk', number> = {
chicken: 18,
durk: 16
}数组和元组
数组和元组,数组和元组都是有序的集合,数组和元组可以包含多个元素,元素类型可以任意,元组可以指定元素类型和数量。
| 特征 | 数组 | 元组 |
|---|---|---|
| 长度约束 | 不限制 | 声明时确定长度 |
| 类型约束 | 不限制 | 确定类型和数量 |
| 赋值约束 | 不限制 | 赋值时类型和数量必须一致 |
| 越界访问 | undefined | 报错 |
| 使用场景 | 存储同类数据(如列表、数据集) | 存储结构化数据(如坐标、返回值、键值对) |
数组
// 数组类型的声明方式
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["hello", "world"];
let numbers2: number[] = Array.from({length: 10}).map((_, index) => index + 1)
// 联合类型数组
let mixed: (number | string)[] = [1, "hello", 2, "world"];
// 只读数组
let readonlyNumbers: readonly number[] = [1, 2, 3];
let readonlyStrings: ReadonlyArray<string> = ["a", "b", "c"];
// 多维数组
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];元组
// 基本元组
let person: [string, number] = ["张进喜", 25];
// 可选元素
let optional: [string, number?] = ["hello"];
// 剩余元素
let rest: [string, ...number[]] = ["hello", 1, 2, 3];
// 只读元组
type StringNumberBooleans = readonly [string, number, ...boolean[]];
const tuple: StringNumberBooleans = ["world", 3, true, false, true];
// 命名元组
type Range = [start: number, end: number];
let range: Range = [0, 100];
// 元组解构
let [name, age] = person;
console.log(`姓名: ${name}, 年龄: ${age}`);枚举
枚举(Enum)是一种数据结构,用于定义一组命名的常量。枚举可以让代码更具可读性,并且提供类型安全。
数字枚举
数字枚举是最常见的枚举类型,成员的值是数字。如果不指定初始值,第一个成员默认为 0,后续成员自动递增。
// 默认从 0 开始
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 指定初始值
enum Status {
Pending = 1,
Approved, // 2
Rejected // 3
}
// 使用枚举
const direction = Direction.Up;
console.log(direction); // 0字符串枚举
字符串枚举的每个成员都必须用字符串字面量初始化。
enum Color {
Red = "#ff0000",
Green = "#00ff00",
Blue = "#0000ff"
}
enum UserRole {
Admin = "admin",
User = "user",
Guest = "guest"
}
// 使用枚举
const role = UserRole.Admin;
console.log(role); // "admin"异构枚举
混合数字和字符串的枚举(不推荐使用)。
enum Mixed {
No = 0,
Yes = "yes"
}常量枚举
使用 const enum 声明的枚举在编译时会被内联,不会生成 JavaScript 代码。
const enum Direction {
Up,
Down,
Left,
Right
}
// 编译后直接使用值,不会生成枚举对象
const direction = Direction.Up; // 编译为: const direction = 0;枚举成员类型
枚举成员可以作为类型使用。
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}反向映射
数字枚举支持反向映射(通过值获取键名),字符串枚举不支持。
enum Direction {
Up = 1,
Down,
Left,
Right
}
console.log(Direction.Up); // 1
console.log(Direction[1]); // "Up" (反向映射)
// 字符串枚举不支持反向映射
enum Color {
Red = "red"
}
// Color["red"] // 错误:无法通过值获取键名枚举使用场景
- 状态管理:定义应用的状态值
- 配置选项:定义配置选项的常量
- 类型安全:替代魔法字符串,提供类型检查
- 代码可读性:使用有意义的名称替代数字
// 状态管理示例
enum LoadingState {
Idle = "idle",
Loading = "loading",
Success = "success",
Error = "error"
}
function handleState(state: LoadingState) {
switch (state) {
case LoadingState.Idle:
// ...
break;
case LoadingState.Loading:
// ...
break;
}
}类型断言
类型断言,用于告诉编译器变量的类型,编译器会忽略类型检查。
const container = document.getElementById("container") as HTMLDivElement;
const name: string | null = 'zhangsan'
name.length // 错误: 类型“string | null”上不存在属性“length”
(name as string).length // 正确字面类型
字面类型,用于定义一个值只能是某个具体的值。
let x: "hello" = "hello";
// OK
x = "hello";
// 错误:值“howdy”与类型“"hello"`不匹配
x = "howdy";显然,这种类型是无意义的对于绝大多数场景来说 但可以结合之前使用过的联合类型
function printText(s: string, alignment: "left" | "right" | "center") {
// ...
}
printText("Hello, world", "left");
printText("G'day, mate", "centre");// 错误:值"centre"与类型""left" | "right" | "center"`不匹配交叉类型
交叉类型(Intersection Types)使用 & 操作符,将多个类型合并为一个类型,新类型包含所有类型的特性。
基本用法
// 合并两个对象类型
type Person = {
name: string;
age: number;
}
type Employee = {
employeeId: string;
department: string;
}
// 交叉类型:同时拥有 Person 和 Employee 的所有属性
type EmployeePerson = Person & Employee;
const employee: EmployeePerson = {
name: "张三",
age: 30,
employeeId: "E001",
department: "技术部"
}交叉类型与联合类型的区别
// 联合类型:类型A 或 类型B(满足其中一个即可)
type StringOrNumber = string | number;
// 交叉类型:类型A 且 类型B(必须同时满足)
type StringAndNumber = string & number; // 永远不可能,因为不存在既是 string 又是 number 的值函数类型交叉
type Loggable = {
log: (message: string) => void;
}
type Serializable = {
serialize: () => string;
}
// 交叉类型:同时拥有两个方法
type Logger = Loggable & Serializable;
const logger: Logger = {
log: (message: string) => console.log(message),
serialize: () => JSON.stringify({})
}接口交叉
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
// 交叉类型:同时实现两个接口
type Duck = Flyable & Swimmable;
const duck: Duck = {
fly: () => console.log("Flying"),
swim: () => console.log("Swimming")
}处理属性冲突
当交叉类型中的属性有冲突时,TypeScript 会尝试合并这些属性。
type A = {
x: string;
y: number;
}
type B = {
x: number; // 与 A 中的 x 类型冲突
z: boolean;
}
// 冲突的属性会被合并为 never 类型(因为 string & number 不可能)
type C = A & B;
// C 的 x 类型为: string & number (即 never)
// C 的 y 类型为: number
// C 的 z 类型为: boolean实际应用场景
- 混入(Mixin)模式:组合多个功能
type Timestamped = {
timestamp: number;
}
type Serializable = {
serialize(): string;
}
// 组合多个功能
type TimestampedSerializable = Timestamped & Serializable;
class MyClass implements TimestampedSerializable {
timestamp: number = Date.now();
serialize(): string {
return JSON.stringify(this);
}
}- 扩展类型:在现有类型基础上添加新属性
type BaseUser = {
id: string;
name: string;
}
type AdminUser = BaseUser & {
permissions: string[];
role: "admin";
}
type RegularUser = BaseUser & {
role: "user";
}
type User = AdminUser | RegularUser;- 工具类型组合:结合 TypeScript 工具类型
type PartialUser = Partial<{
name: string;
age: number;
}> & {
id: string; // id 保持必需
}