Skip to content

ts基础

基本数据类型

ts
// 字符串类型
const name: string = 'zhangsan'
// 数值类型
const age: number = 15
const hex: number = 0xff

// 布尔类型
const adult: boolean = false
const graduated: boolean = 0

any类型

any,可以是如何类型,不推荐使用

ts
let anyData: any = 123
anyData = 'zhangsan'
anyData = true

unknown类型

unkonwn,表示未知类型,会自动推导第一次赋值的类型

ts
let unkonwnData: unkonwn
unkonwnData = 123
unkonwnData = 'zhangsan' // 报错: 类型“string”的参数不能赋给类型“number”的参数

void类型

void,表示空类型,表示没有返回值

ts
function print(): void {
    console.log('hello world')
}

never类型

never,表示永远不会有返回的类型

  • 如抛出异常
  • 死循环
ts
function error(message: string): never {
    throw new Error(message)
}

function infiniteLoop(): never {
    while (true) {
    }
}

使用场景

它是所有类型的子类型,但没有任何类型是它的子类型(除了自身),主要用于:

  • 永远不结束的函数:如抛错函数、无限循环函数;
  • 类型收窄到不存在:结合条件判断,标记「不可能触发的分支」;
  • 排除联合类型的所有可能:实现严格的类型穷尽检查。
vue

<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类型

ts
let name: string | null = null
let age: number | undefined = undefined

javaScript 有两个基础值用于表示值不存在或未初始化的值:null 和 undefined。 TypeScript 有两个对应的同名类型。这些类型的行为取决于你是否启用了 strictNullChecks 选项。

关闭 strictNullChecks,可能是 nullundefined 的值仍然可以正常访问,并且值 nullundefined 可以分配给任何类型的属性。缺乏对这些值的检查往往是错误的主要来源;

启用 strictNullChecks 时,当值为 null 或 undefined 时,你需要在对该值使用方法或属性之前测试这些值。就像在使用可选属性之前检查 undefined 一样,我们可以使用缩小来检查可能是 null 的值:

ts
function doSomething(x: string | null) {
    if (x === null) {
        // do nothing
    } else {
        console.log("Hello, " + x.toUpperCase());
    }
}

联合类型

联合类型,表示多个类型中的一个

ts
let name: string | number = 'zhangsan'
name = 123
name = true // 错误: 类型“boolean”的参数不能赋给类型“string | number”的参数

非空断言

TypeScript 还具有一种特殊的语法,可以在不进行任何显式检查的情况下从类型中删除 null 和 undefined。在任何表达式之后写 ! 实际上是一个类型断言,该值不是 null 或 undefined:

ts
let name: string | null = null
name!.toUpperCase()
name.toUpperCase() // 错误: 类型“null”上不存在属性“toUpperCase”

对象类型

创建对象类型,对象类型可以包含多个属性,属性类型可以任意,属性名可以任意

ts
// 固定类型
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报错
使用场景存储同类数据(如列表、数据集)存储结构化数据(如坐标、返回值、键值对)

数组

ts
// 数组类型的声明方式
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]
];

元组

ts
// 基本元组
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,后续成员自动递增。

ts
// 默认从 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

字符串枚举

字符串枚举的每个成员都必须用字符串字面量初始化。

ts
enum Color {
    Red = "#ff0000",
    Green = "#00ff00",
    Blue = "#0000ff"
}

enum UserRole {
    Admin = "admin",
    User = "user",
    Guest = "guest"
}

// 使用枚举
const role = UserRole.Admin;
console.log(role); // "admin"

异构枚举

混合数字和字符串的枚举(不推荐使用)。

ts
enum Mixed {
    No = 0,
    Yes = "yes"
}

常量枚举

使用 const enum 声明的枚举在编译时会被内联,不会生成 JavaScript 代码。

ts
const enum Direction {
    Up,
    Down,
    Left,
    Right
}

// 编译后直接使用值,不会生成枚举对象
const direction = Direction.Up; // 编译为: const direction = 0;

枚举成员类型

枚举成员可以作为类型使用。

ts
enum ShapeKind {
    Circle,
    Square,
}

interface Circle {
    kind: ShapeKind.Circle;
    radius: number;
}

interface Square {
    kind: ShapeKind.Square;
    sideLength: number;
}

反向映射

数字枚举支持反向映射(通过值获取键名),字符串枚举不支持。

ts
enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}

console.log(Direction.Up);    // 1
console.log(Direction[1]);    // "Up" (反向映射)

// 字符串枚举不支持反向映射
enum Color {
    Red = "red"
}
// Color["red"] // 错误:无法通过值获取键名

枚举使用场景

  1. 状态管理:定义应用的状态值
  2. 配置选项:定义配置选项的常量
  3. 类型安全:替代魔法字符串,提供类型检查
  4. 代码可读性:使用有意义的名称替代数字
ts
// 状态管理示例
enum LoadingState {
    Idle = "idle",
    Loading = "loading",
    Success = "success",
    Error = "error"
}

function handleState(state: LoadingState) {
    switch (state) {
        case LoadingState.Idle:
            // ...
            break;
        case LoadingState.Loading:
            // ...
            break;
    }
}

类型断言

类型断言,用于告诉编译器变量的类型,编译器会忽略类型检查。

ts
const container = document.getElementById("container") as HTMLDivElement;

const name: string | null = 'zhangsan'
name.length // 错误: 类型“string | null”上不存在属性“length”
    (name as string).length // 正确

字面类型

字面类型,用于定义一个值只能是某个具体的值。

ts
let x: "hello" = "hello";
// OK
x = "hello";
// 错误:值“howdy”与类型“"hello"`不匹配
x = "howdy";

显然,这种类型是无意义的对于绝大多数场景来说 但可以结合之前使用过的联合类型

ts
function printText(s: string, alignment: "left" | "right" | "center") {
    // ...
}

printText("Hello, world", "left");
printText("G'day, mate", "centre");// 错误:值"centre"与类型""left" | "right" | "center"`不匹配

交叉类型

交叉类型(Intersection Types)使用 & 操作符,将多个类型合并为一个类型,新类型包含所有类型的特性。

基本用法

ts
// 合并两个对象类型
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: "技术部"
}

交叉类型与联合类型的区别

ts
// 联合类型:类型A 或 类型B(满足其中一个即可)
type StringOrNumber = string | number;

// 交叉类型:类型A 且 类型B(必须同时满足)
type StringAndNumber = string & number; // 永远不可能,因为不存在既是 string 又是 number 的值

函数类型交叉

ts
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({})
}

接口交叉

ts
interface Flyable {
    fly(): void;
}

interface Swimmable {
    swim(): void;
}

// 交叉类型:同时实现两个接口
type Duck = Flyable & Swimmable;

const duck: Duck = {
    fly: () => console.log("Flying"),
    swim: () => console.log("Swimming")
}

处理属性冲突

当交叉类型中的属性有冲突时,TypeScript 会尝试合并这些属性。

ts
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

实际应用场景

  1. 混入(Mixin)模式:组合多个功能
ts
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);
    }
}
  1. 扩展类型:在现有类型基础上添加新属性
ts
type BaseUser = {
    id: string;
    name: string;
}

type AdminUser = BaseUser & {
    permissions: string[];
    role: "admin";
}

type RegularUser = BaseUser & {
    role: "user";
}

type User = AdminUser | RegularUser;
  1. 工具类型组合:结合 TypeScript 工具类型
ts
type PartialUser = Partial<{
    name: string;
    age: number;
}> & {
    id: string;  // id 保持必需
}

这是我的个人文档