Decade
Decade
Published on 2025-10-09 / 15 Visits
0
0

TS主要类型(未完成)

一、基础类型介绍

1.1 类型系统核心概念

TypeScript 的类型系统是其最强大的特性之一,理解各种类型的区别对于编写类型安全的代码至关重要。

1.2 any、unknown、Object、object 的区别

// any:任意类型,关闭类型检查
let anyValue: any = 42;
anyValue = "hello";
anyValue = { name: "test" };
anyValue.foo.bar.baz; // 不会报错,但运行时可能出错

// unknown:未知类型(top type),类型安全的 any
let unknownValue: unknown = 42;
unknownValue = "hello";
unknownValue = { name: "test" };
// unknownValue.foo; // 错误!需要类型断言或类型守卫

// Object:万物之父,所有引用类型的基类
let objValue: Object = { name: "test" };
objValue = [1, 2, 3];
objValue = new Date();
objValue = 42; // 也可以,因为 number 有对应的包装对象

// object:表示引用类型,不包括基础类型
let objectValue: object = { name: "test" };
objectValue = [1, 2, 3];
objectValue = new Date();
// objectValue = 42; // 错误!不能赋值基础类型

重要说明: unknown 只能赋值给 unknownany,不能直接赋值给明确类型,也不能访问其属性。使用前需要进行类型检查或类型断言。

1.3 基础类型与包装类型

// 基础类型(小写)
let num: number = 123;
let str: string = "hello";
let bool: boolean = true;

// 包装类型(大写)- 不推荐使用
let numObj: Number = new Number(123);
let strObj: String = new String("hello");
let boolObj: Boolean = new Boolean(true);

// 注意:基础类型不等于包装类型
// let x: number = new Number(123); // 错误!

说明: 在 TypeScript 中应该优先使用小写的基础类型(numberstringboolean),而不是大写的包装类型。

1.4 never 类型

// never 表示永远不会有返回值的类型
function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // 无限循环
  }
}

// never 是所有类型的子类型
type Result = string | number | never; // Result 等价于 string | number

说明: never 类型表示那些永不存在的值的类型,常用于抛出异常或无限循环的函数。

二、示例与说明

2.1 Object 与 object 的实际应用

let w: number = 2312; // 基础类型
let a: Object = { name: 'w' }; // Object 类型
let b: object = { name: 'w' }; // object 类型
let c: {} = { name: 'w' }; // 空对象类型

// 重要:let a: {} 等价于 let a: Object
console.log(typeof a); // "object"
console.log(typeof w); // "number"

解释:

  • Object 可以表示任意引用类型,包括基础类型的包装对象

  • object 表示非原始类型,不包括 numberstringbooleansymbolnullundefined

  • {} 空对象类型等价于 Object,表示除了 nullundefined 之外的所有类型

三、接口(Interface)

3.1 接口的作用

接口是 TypeScript 的核心特性之一,用于定义对象的结构和约束类型,确保代码的类型安全性和可维护性。

3.2 基本接口定义

interface testDb {
  name: string;
  readonly age?: number; // 可选且只读属性
  address: string;
  // [key: string]: any; // 索引签名,可扩展任意属性
}

// 使用接口
let user: testDb = {
  name: "张三",
  age: 25, // 可选属性
  address: "北京市"
};

// user.age = 26; // 错误!age 是只读属性

说明:

  • ? 表示可选属性,该属性可以不存在

  • readonly 表示只读属性,初始化后不能修改

  • 索引签名允许对象包含任意数量的其他属性

3.3 接口数组

interface testArray {
  name: string;
  age: number;
}

let List: testArray[] = [
  { name: "w", age: 23 },
  { name: "张三", age: 25 },
  { name: "李四", age: 30 }
];

console.log(List[0]?.age); // 23
console.log(List[3]?.age); // undefined(安全访问)

说明: 接口数组用于定义数组中每个元素的结构,确保数组中的所有对象都符合接口定义。

3.4 接口继承

interface bb extends testDb {
  me: string;
}

let xiaoming: bb = {
  name: 'xiao',
  age: 2,       // 继承的可选属性
  address: "test",
  me: 'wd',     // 新增的必需属性
};

// 多重继承
interface Person {
  name: string;
}

interface Employee {
  employeeId: number;
}

interface Manager extends Person, Employee {
  department: string;
}

说明: 接口可以通过 extends 关键字继承其他接口,实现接口的复用和扩展。

四、函数接口

4.1 函数类型接口定义

interface Fn {
  (name: string): number[];
}

let pp: Fn = (a: string) => {
  console.log(`处理参数: ${a}`);
  return [1, 2, 3];
};

// 更复杂的函数接口
interface Calculator {
  (a: number, b: number): number;
  description?: string; // 函数也可以有属性
}

let add: Calculator = (x, y) => x + y;
add.description = "加法运算";

说明: 函数接口定义了函数的参数类型和返回值类型,确保函数的输入输出符合预期。

五、数组类型

5.1 数组类型的多种定义方式

// 方式一:使用 Array<T> 泛型
let array: Array<number> = [1, 2, 3, 4, 5];

// 方式二:使用 T[] 语法
let array2: number[] = [1, 2, 3, 4, 5];

// 方式三:使用接口定义数组
interface NumberArray {
  [index: number]: number;
}
let array3: NumberArray = [1, 2, 3, 4, 5];

// 遍历数组
array.forEach(element => {
  console.log(element);
});

说明: TypeScript 提供了多种方式来定义数组类型,推荐使用 T[] 语法,简洁明了。

六、函数参数与可变参数

6.1 可变参数(Rest Parameters)

function testFunc(...args: number[]): string {
  let re = args;
  console.log(re); // [1, 2, 3, 4, 5, 6]
  return "返回";
}

let wdaw: number[] = [1, 2, 3, 4, 5, 6];
// testFunc(wdaw); // 错误!不可直接传数组
testFunc(...wdaw); // 正确!使用展开运算符
testFunc(1, 2, 3, 4, 5, 6); // 正确!直接传递多个参数

// 混合参数
function mixedParams(first: string, ...rest: number[]): void {
  console.log(first); // "hello"
  console.log(rest);  // [1, 2, 3]
}

mixedParams("hello", 1, 2, 3);

说明: 可变参数 ...args 必须是参数列表的最后一个参数,类似于 C# 的 params 关键字。

七、自定义索引类型

7.1 索引签名

interface llist {
  1: number;                  // 数字索引
  [key: string]: number;       // 字符串索引签名
}

let obj2: llist = {
  1: 232,
  "temperature": 36.5,
  "height": 180,
  name: 23  // 字符串索引
};

// 访问属性
console.log(obj2[1]);           // 232
console.log(obj2["temperature"]); // 36.5
console.log(obj2.height);       // 180

说明: 索引签名允许对象包含任意数量的属性,但所有属性值必须符合指定的类型。

7.2 接口数组的高级用法

let FaceArray: llist[] = [
  { 1: 232, "temperature": 36.5, "height": 180, name: 23 },
  { 1: 100, "temperature": 37.0, "height": 175, name: 25 }
];

console.log(FaceArray[0]);           // 整个对象
console.log(FaceArray[0]?.name);     // 23 或 undefined
console.log(FaceArray[0]?.height);   // 180 或 undefined
console.log(FaceArray[0]?.["name"]); // 23 或 undefined

// 遍历接口数组
FaceArray.forEach((item, index) => {
  console.log(`第 ${index} 个对象的 name: ${item.name}`);
});

说明: 使用可选链操作符 ?. 可以安全地访问可能不存在的属性,避免运行时错误。

八、函数内部的 arguments

8.1 arguments 对象

// JavaScript 传统方式(不推荐在 TypeScript 中使用)
function oldStyle() {
  console.log(arguments); // IArguments 类型
  // arguments 是类数组对象,不是真正的数组
  // Array.prototype.slice.call(arguments) 转换为数组
}

// TypeScript 推荐方式
function newStyle(...args: any[]): void {
  console.log(args); // 真正的数组
  args.forEach(arg => {
    console.log(arg);
  });
}

// 类型安全的可变参数
function typeSafe(...numbers: number[]): number {
  return numbers.reduce((sum, n) => sum + n, 0);
}

console.log(typeSafe(1, 2, 3, 4, 5)); // 15

说明:

  • arguments 是一个类数组对象,包含函数调用时的所有参数

  • 在 TypeScript 中使用 ...args 剩余参数更安全、更清晰

  • arguments 在箭头函数中不可用,而 ...args 可以正常使用

  • TypeScript 函数学习笔记

    九 、基础函数定义

    普通函数

    typescript

    function add(a: number, b: number): number {
        return a + b
    }
    console.log(add(1, 2)) // 3

    箭头函数

    typescript

    let fu = (a: number, b: number): number => a + b
    console.log(fu(3, 2)) // 5

    要点:

    • 参数需要类型注解 a: number

    • 返回值类型可写可不写(TypeScript 会推断)

    • 箭头函数更简洁,适合简单逻辑


    2. 使用接口约束对象类型

    问题:对象内部函数如何访问自身属性?

    typescript

    interface FunctionTeach {
        numList: number[]
        add: (this: FunctionTeach, value: number) => void
    }
    
    let testObj: FunctionTeach = {
        numList: [1, 2, 3, 4, 5],
        add(this: FunctionTeach, value: number) {
            this.numList.push(value) // 安全访问自身属性
            console.log('添加后:', this.numList)
        }
    }
    
    testObj.add(6) // [1, 2, 3, 4, 5, 6]

    this 参数的秘密

    重要概念:

    • this 是一个伪参数,调用时不需要传递

    • 它只用于告诉 TypeScript:"这个函数应该在什么对象上调用"

    • 提供类型安全,防止 this 指向错误

    示例:

    typescript

    interface Calculator {
        value: number
        add: (this: Calculator, n: number) => void
    }
    
    let calc: Calculator = {
        value: 0,
        add(this: Calculator, n: number) {
            this.value += n // this 有完整的类型提示
        }
    }

    3. 函数重载 - 一个函数,多种用法

    核心思想

    同一个函数,根据不同的参数类型,返回不同的结果。

    基本语法

    typescript

    // 第 1 步:声明重载签名(不实现)
    function process(value: string): string
    function process(value: number): number
    function process(value: number[]): number
    
    // 第 2 步:实现签名(必须兼容所有重载)
    function process(value: string | number | number[]): string | number {
        if (typeof value === 'string') {
            return value.toUpperCase()
        } else if (typeof value === 'number') {
            return value * 2
        } else {
            return value.reduce((sum, num) => sum + num, 0)
        }
    }
    
    // 使用
    console.log(process('hello'))      // HELLO (string)
    console.log(process(5))            // 10 (number)
    console.log(process([1, 2, 3]))    // 6 (number)

    更实用的例子:创建用户

    typescript

    interface User {
        name: string
        age: number
    }
    
    // 方式 1:传入 name 和 age
    function createUser(name: string, age: number): User
    
    // 方式 2:直接传入 User 对象
    function createUser(user: User): User
    
    // 实现:根据参数类型判断
    function createUser(nameOrUser: string | User, age?: number): User {
        if (typeof nameOrUser === 'string') {
            return { name: nameOrUser, age: age! }
        } else {
            return nameOrUser
        }
    }
    
    // 两种调用方式都可以
    const user1 = createUser('张三', 25)
    const user2 = createUser({ name: '李四', age: 30 })

    重载的注意事项

    1. 重载签名不实现,实现签名不被直接调用

    2. 实现签名必须兼容所有重载签名

    3. 重载顺序很重要 - 从具体到一般(TypeScript 从上往下匹配)

    4. 类似 C# 的 switch-case - 在实现函数中判断类型分支


    4. 其他常用函数特性

    可选参数

    typescript

    function greet(name: string, greeting?: string): string {
        return `${greeting || '你好'}, ${name}!`
    }
    
    greet('小明')          // 你好, 小明!
    greet('小红', '早上好') // 早上好, 小红!

    默认参数

    typescript

    function greet(name: string, greeting: string = '你好'): string {
        return `${greeting}, ${name}!`
    }
    
    greet('小明') // 你好, 小明!

    剩余参数

    typescript

    function sum(...numbers: number[]): number {
        return numbers.reduce((total, num) => total + num, 0)
    }
    
    sum(1, 2, 3, 4, 5) // 15

    5. 快速总结

    特性用途关键点普通函数定义可复用逻辑function name(param: type): returnType {}箭头函数简洁的函数表达式(param: type) => returnType接口约束规范对象结构用 interface 定义对象类型this 参数类型安全的 thismethod(this: Type, ...) 伪参数函数重载一个函数多种用法声明 + 实现,根据参数类型分支可选参数参数可传可不传param?: type默认参数参数有默认值param: type = defaultValue剩余参数接收任意数量参数...params: type[]


Comment