一、基础类型介绍
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
只能赋值给 unknown
或 any
,不能直接赋值给明确类型,也不能访问其属性。使用前需要进行类型检查或类型断言。
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 中应该优先使用小写的基础类型(number
、string
、boolean
),而不是大写的包装类型。
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
表示非原始类型,不包括number
、string
、boolean
、symbol
、null
或undefined
{}
空对象类型等价于Object
,表示除了null
和undefined
之外的所有类型
三、接口(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 })
重载的注意事项
重载签名不实现,实现签名不被直接调用
实现签名必须兼容所有重载签名
重载顺序很重要 - 从具体到一般(TypeScript 从上往下匹配)
类似 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[]