Appearance
TypeScript 对象
在 TypeScript 中,对象是一种复合数据类型,用于存储键值对。对象可以包含属性和方法,是 TypeScript 中最常用的数据结构之一。本文将详细介绍 TypeScript 中的对象。
1. 对象的定义
基本语法
使用对象字面量语法定义对象。
typescript
// 基本对象定义
const person = {
name: "John",
age: 30,
gender: "male"
};
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30
console.log(person.gender); // 输出:male对象的类型注解
使用类型注解为对象指定类型。
typescript
// 使用类型注解
interface Person {
name: string;
age: number;
gender: string;
}
const person: Person = {
name: "John",
age: 30,
gender: "male"
};
// 使用类型字面量
const person2: { name: string; age: number; gender: string } = {
name: "Jane",
age: 25,
gender: "female"
};2. 对象的属性
属性访问
使用点号(.)或方括号([])访问对象的属性。
typescript
const person = {
name: "John",
age: 30,
gender: "male"
};
// 使用点号访问
console.log(person.name); // 输出:John
// 使用方括号访问
console.log(person["age"]); // 输出:30
// 使用变量作为属性名
const propertyName = "gender";
console.log(person[propertyName]); // 输出:male属性修改
可以修改对象的属性值。
typescript
const person = {
name: "John",
age: 30,
gender: "male"
};
// 修改属性值
person.name = "Jane";
person["age"] = 25;
console.log(person.name); // 输出:Jane
console.log(person.age); // 输出:25属性添加
可以向对象添加新的属性。
typescript
const person = {
name: "John",
age: 30
};
// 添加新属性
person.gender = "male";
person["email"] = "john@example.com";
console.log(person.gender); // 输出:male
console.log(person.email); // 输出:john@example.com属性删除
使用 delete 操作符删除对象的属性。
typescript
const person = {
name: "John",
age: 30,
gender: "male"
};
// 删除属性
delete person.gender;
console.log(person.gender); // 输出:undefined
console.log("gender" in person); // 输出:false3. 对象的方法
方法定义
对象可以包含方法,方法是对象的函数属性。
typescript
const person = {
name: "John",
age: 30,
greet: function() {
return `Hello, my name is ${this.name}.`;
},
// 箭头函数方法(注意:箭头函数没有自己的 this)
greetArrow: () => {
// 这里的 this 不是指向 person 对象
return "Hello!";
},
// 简写方法
sayAge() {
return `I am ${this.age} years old.`;
}
};
console.log(person.greet()); // 输出:Hello, my name is John.
console.log(person.greetArrow()); // 输出:Hello!
console.log(person.sayAge()); // 输出:I am 30 years old.方法中的 this
在对象方法中,this 指向调用该方法的对象。
typescript
const person = {
name: "John",
age: 30,
greet: function() {
return `Hello, my name is ${this.name}.`;
}
};
const anotherPerson = {
name: "Jane"
};
// 借用方法
const greetFunction = person.greet;
console.log(greetFunction()); // 输出:Hello, my name is undefined.(this 指向全局对象)
// 使用 call 方法设置 this
console.log(greetFunction.call(anotherPerson)); // 输出:Hello, my name is Jane.
// 使用 apply 方法设置 this
console.log(greetFunction.apply(anotherPerson)); // 输出:Hello, my name is Jane.
// 使用 bind 方法绑定 this
const boundGreet = greetFunction.bind(anotherPerson);
console.log(boundGreet()); // 输出:Hello, my name is Jane.4. 对象字面量
基本语法
对象字面量是创建对象的简洁语法。
typescript
// 基本对象字面量
const person = {
name: "John",
age: 30
};
// 计算属性名
const propertyName = "gender";
const person2 = {
name: "John",
[propertyName]: "male"
};
console.log(person2.gender); // 输出:male
// 简写属性名
const name = "John";
const age = 30;
const person3 = {
name,
age
};
console.log(person3.name); // 输出:John
console.log(person3.age); // 输出:30
// 简写方法
const person4 = {
name: "John",
greet() {
return `Hello, my name is ${this.name}.`;
}
};
console.log(person4.greet()); // 输出:Hello, my name is John.对象字面量的类型推断
TypeScript 会根据对象字面量的内容推断其类型。
typescript
// 类型推断为 { name: string; age: number; }
const person = {
name: "John",
age: 30
};
// 类型推断为 { name: string; age: number; gender?: string; }
const person2 = {
name: "John",
age: 30,
gender: "male" // 可选属性
};
// 类型推断为 { name: string; age: number; greet: () => string; }
const person3 = {
name: "John",
age: 30,
greet() {
return `Hello, my name is ${this.name}.`;
}
};5. 对象类型
接口
使用接口定义对象类型。
typescript
interface Person {
name: string;
age: number;
gender?: string; // 可选属性
readonly id: number; // 只读属性
}
const person: Person = {
id: 1,
name: "John",
age: 30
};
// person.id = 2; // 错误:只读属性
person.gender = "male"; // 正确:可选属性类型别名
使用类型别名定义对象类型。
typescript
type Person = {
name: string;
age: number;
gender?: string;
};
const person: Person = {
name: "John",
age: 30,
gender: "male"
};索引签名
使用索引签名定义对象的索引类型。
typescript
// 字符串索引签名
interface StringMap {
[key: string]: string;
}
const map: StringMap = {
name: "John",
age: "30"
};
// 数字索引签名
interface NumberArray {
[index: number]: number;
}
const array: NumberArray = [1, 2, 3, 4, 5];
// 混合索引签名
interface MixedMap {
[key: string]: string | number;
length: number;
}
const mixed: MixedMap = {
name: "John",
age: 30,
length: 2
};6. 对象的解构
基本解构
使用解构语法从对象中提取属性。
typescript
const person = {
name: "John",
age: 30,
gender: "male"
};
// 基本解构
const { name, age } = person;
console.log(name); // 输出:John
console.log(age); // 输出:30
// 重命名变量
const { name: personName, age: personAge } = person;
console.log(personName); // 输出:John
console.log(personAge); // 输出:30
// 默认值
const { name, age, city = "New York" } = person;
console.log(city); // 输出:New York
// 嵌套解构
const personWithAddress = {
name: "John",
age: 30,
address: {
street: "123 Main St",
city: "New York"
}
};
const { address: { street, city } } = personWithAddress;
console.log(street); // 输出:123 Main St
console.log(city); // 输出:New York函数参数解构
在函数参数中使用解构语法。
typescript
interface Person {
name: string;
age: number;
gender?: string;
}
// 函数参数解构
function greet({ name, age, gender = "unknown" }: Person): string {
return `Hello, ${name}! You are ${age} years old and your gender is ${gender}.`;
}
const person = {
name: "John",
age: 30,
gender: "male"
};
console.log(greet(person)); // 输出:Hello, John! You are 30 years old and your gender is male.7. 对象的扩展运算符
扩展对象
使用扩展运算符(...)扩展对象。
typescript
const person = {
name: "John",
age: 30
};
// 复制对象
const personCopy = { ...person };
console.log(personCopy); // 输出:{ name: 'John', age: 30 }
// 合并对象
const personWithAddress = {
...person,
address: "123 Main St"
};
console.log(personWithAddress); // 输出:{ name: 'John', age: 30, address: '123 Main St' }
// 覆盖属性
const updatedPerson = {
...person,
age: 31
};
console.log(updatedPerson); // 输出:{ name: 'John', age: 31 }
// 合并多个对象
const person1 = { name: "John" };
const person2 = { age: 30 };
const person3 = { gender: "male" };
const combinedPerson = { ...person1, ...person2, ...person3 };
console.log(combinedPerson); // 输出:{ name: 'John', age: 30, gender: 'male' }剩余属性
使用剩余运算符(...)收集剩余属性。
typescript
const person = {
name: "John",
age: 30,
gender: "male",
email: "john@example.com"
};
// 提取部分属性,剩余属性放入 rest
const { name, age, ...rest } = person;
console.log(name); // 输出:John
console.log(age); // 输出:30
console.log(rest); // 输出:{ gender: 'male', email: 'john@example.com' }8. 对象的类型守卫
typeof 类型守卫
使用 typeof 操作符进行类型守卫。
typescript
interface Person {
name: string;
age: number;
}
function processValue(value: string | number | Person) {
if (typeof value === "string") {
console.log(`String: ${value}`);
} else if (typeof value === "number") {
console.log(`Number: ${value}`);
} else {
console.log(`Person: ${value.name}, ${value.age}`);
}
}
processValue("John"); // 输出:String: John
processValue(30); // 输出:Number: 30
processValue({ name: "John", age: 30 }); // 输出:Person: John, 30instanceof 类型守卫
使用 instanceof 操作符进行类型守卫。
typescript
class Person {
constructor(public name: string, public age: number) {}
}
class Animal {
constructor(public name: string, public species: string) {}
}
function processObject(obj: Person | Animal) {
if (obj instanceof Person) {
console.log(`Person: ${obj.name}, ${obj.age}`);
} else if (obj instanceof Animal) {
console.log(`Animal: ${obj.name}, ${obj.species}`);
}
}
processObject(new Person("John", 30)); // 输出:Person: John, 30
processObject(new Animal("Rex", "Dog")); // 输出:Animal: Rex, Dogin 操作符类型守卫
使用 in 操作符进行类型守卫。
typescript
interface Person {
name: string;
age: number;
}
interface Animal {
name: string;
species: string;
}
function processObject(obj: Person | Animal) {
if ("age" in obj) {
console.log(`Person: ${obj.name}, ${obj.age}`);
} else if ("species" in obj) {
console.log(`Animal: ${obj.name}, ${obj.species}`);
}
}
processObject({ name: "John", age: 30 }); // 输出:Person: John, 30
processObject({ name: "Rex", species: "Dog" }); // 输出:Animal: Rex, Dog自定义类型守卫
使用自定义类型守卫函数进行类型守卫。
typescript
interface Person {
name: string;
age: number;
}
interface Animal {
name: string;
species: string;
}
function isPerson(obj: any): obj is Person {
return typeof obj === "object" && obj !== null && "age" in obj;
}
function isAnimal(obj: any): obj is Animal {
return typeof obj === "object" && obj !== null && "species" in obj;
}
function processObject(obj: Person | Animal) {
if (isPerson(obj)) {
console.log(`Person: ${obj.name}, ${obj.age}`);
} else if (isAnimal(obj)) {
console.log(`Animal: ${obj.name}, ${obj.species}`);
}
}
processObject({ name: "John", age: 30 }); // 输出:Person: John, 30
processObject({ name: "Rex", species: "Dog" }); // 输出:Animal: Rex, Dog9. 对象的最佳实践
1. 使用接口定义对象类型
使用接口定义对象类型,提高代码的可读性和类型安全性。
typescript
// 推荐:使用接口
interface User {
id: number;
name: string;
email: string;
}
function getUser(id: number): User {
// 实现
}
// 不推荐:直接使用类型字面量
function getUser(id: number): { id: number; name: string; email: string } {
// 实现
}2. 使用可选属性和只读属性
对于可能不存在的属性,使用可选属性;对于不应该被修改的属性,使用只读属性。
typescript
// 推荐:使用可选属性和只读属性
interface User {
readonly id: number;
name: string;
email: string;
age?: number;
}
// 不推荐:使用普通属性
interface User {
id: number;
name: string;
email: string;
age: number | undefined;
}3. 使用解构简化代码
使用解构语法简化代码,提高可读性。
typescript
// 推荐:使用解构
function greet({ name, age }: { name: string; age: number }): string {
return `Hello, ${name}! You are ${age} years old.`;
}
// 不推荐:不使用解构
function greet(person: { name: string; age: number }): string {
return `Hello, ${person.name}! You are ${person.age} years old.`;
}4. 使用扩展运算符合并对象
使用扩展运算符合并对象,简化代码。
typescript
// 推荐:使用扩展运算符
const person = { name: "John", age: 30 };
const updatedPerson = { ...person, age: 31 };
// 不推荐:手动复制属性
const person = { name: "John", age: 30 };
const updatedPerson = { name: person.name, age: 31 };5. 使用类型守卫处理联合类型
使用类型守卫处理联合类型,提高代码的类型安全性。
typescript
// 推荐:使用类型守卫
function processValue(value: string | number | Person) {
if (typeof value === "string") {
// 处理字符串
} else if (typeof value === "number") {
// 处理数字
} else {
// 处理 Person 对象
}
}
// 不推荐:不使用类型守卫
function processValue(value: string | number | Person) {
// 可能会导致类型错误
}10. 常见错误
1. 属性访问错误
尝试访问不存在的属性会导致 undefined。
typescript
const person = {
name: "John",
age: 30
};
// 错误:访问不存在的属性
console.log(person.gender); // 输出:undefined
// 正确:使用可选链运算符
console.log(person?.gender); // 输出:undefined
// 正确:使用 in 操作符检查属性是否存在
if ("gender" in person) {
console.log(person.gender);
}2. 类型错误
属性类型不匹配会导致类型错误。
typescript
interface Person {
name: string;
age: number;
}
// 错误:类型不匹配
// const person: Person = {
// name: "John",
// age: "30" // 类型 'string' 不能赋值给类型 'number'
// };
// 正确:类型匹配
const person: Person = {
name: "John",
age: 30
};3. 只读属性修改错误
尝试修改只读属性会导致错误。
typescript
interface Person {
readonly id: number;
name: string;
age: number;
}
const person: Person = {
id: 1,
name: "John",
age: 30
};
// 错误:修改只读属性
// person.id = 2; // 无法分配到 "id" ,因为它是只读属性
// 正确:修改非只读属性
person.name = "Jane";
person.age = 25;4. this 指向错误
在箭头函数中,this 不指向调用对象。
typescript
const person = {
name: "John",
age: 30,
// 错误:箭头函数中的 this 不是指向 person 对象
greet: () => {
return `Hello, my name is ${this.name}.`; // this.name 是 undefined
},
// 正确:使用普通函数
greetCorrect() {
return `Hello, my name is ${this.name}.`;
}
};
console.log(person.greet()); // 输出:Hello, my name is undefined.
console.log(person.greetCorrect()); // 输出:Hello, my name is John.5. 对象解构错误
解构时属性名不匹配会导致 undefined。
typescript
const person = {
name: "John",
age: 30
};
// 错误:解构时属性名不匹配
const { name, years } = person;
console.log(years); // 输出:undefined
// 正确:解构时使用正确的属性名
const { name, age } = person;
console.log(age); // 输出:3011. 实际应用场景
1. 定义配置对象
typescript
interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
database: string;
ssl?: boolean;
poolSize?: number;
}
const config: DatabaseConfig = {
host: "localhost",
port: 5432,
username: "postgres",
password: "password",
database: "mydb",
ssl: false,
poolSize: 10
};
function connectToDatabase(config: DatabaseConfig): void {
console.log(`Connecting to ${config.database} on ${config.host}:${config.port}`);
// 实现连接逻辑
}
connectToDatabase(config);2. 处理 API 响应
typescript
interface User {
id: number;
name: string;
email: string;
createdAt: string;
updatedAt: string;
}
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
async function fetchUser(id: number): Promise<ApiResponse<User>> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return data;
}
fetchUser(1).then((response) => {
const { data, status, message } = response;
console.log(`User: ${data.name}, Status: ${status}, Message: ${message}`);
});3. 实现状态管理
typescript
interface State {
user: {
id: number;
name: string;
email: string;
} | null;
isLoading: boolean;
error: string | null;
}
const initialState: State = {
user: null,
isLoading: false,
error: null
};
function reducer(state: State, action: any): State {
switch (action.type) {
case "FETCH_USER_START":
return {
...state,
isLoading: true,
error: null
};
case "FETCH_USER_SUCCESS":
return {
...state,
user: action.payload,
isLoading: false,
error: null
};
case "FETCH_USER_ERROR":
return {
...state,
isLoading: false,
error: action.payload
};
default:
return state;
}
}
// 使用
let state = initialState;
console.log(state); // 初始状态
state = reducer(state, { type: "FETCH_USER_START" });
console.log(state); // 加载状态
state = reducer(state, {
type: "FETCH_USER_SUCCESS",
payload: { id: 1, name: "John", email: "john@example.com" }
});
console.log(state); // 成功状态4. 实现选项对象
typescript
interface Options {
timeout?: number;
retry?: number;
headers?: Record<string, string>;
body?: any;
method?: "GET" | "POST" | "PUT" | "DELETE";
}
function fetchWithOptions(url: string, options: Options = {}): Promise<any> {
const {
timeout = 30000,
retry = 3,
headers = { "Content-Type": "application/json" },
body,
method = "GET"
} = options;
console.log(`Fetching ${url} with method ${method}`);
console.log(`Timeout: ${timeout}ms, Retry: ${retry} times`);
console.log(`Headers:`, headers);
console.log(`Body:`, body);
// 实现 fetch 逻辑
return fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined
});
}
// 使用
fetchWithOptions("https://api.example.com/users", {
method: "POST",
body: { name: "John", email: "john@example.com" },
timeout: 60000
});5. 实现工具函数
typescript
interface Person {
name: string;
age: number;
gender: string;
}
function sortByProperty<T>(array: T[], property: keyof T): T[] {
return array.sort((a, b) => {
if (a[property] < b[property]) return -1;
if (a[property] > b[property]) return 1;
return 0;
});
}
const people: Person[] = [
{ name: "John", age: 30, gender: "male" },
{ name: "Jane", age: 25, gender: "female" },
{ name: "Bob", age: 35, gender: "male" }
];
// 按年龄排序
const sortedByAge = sortByProperty(people, "age");
console.log(sortedByAge);
// 按名字排序
const sortedByName = sortByProperty(people, "name");
console.log(sortedByName);总结
TypeScript 中的对象是一种复合数据类型,用于存储键值对。本文介绍了 TypeScript 对象的以下内容:
- 对象的定义:基本语法,对象的类型注解
- 对象的属性:属性访问,属性修改,属性添加,属性删除
- 对象的方法:方法定义,方法中的 this
- 对象字面量:基本语法,计算属性名,简写属性名,简写方法,对象字面量的类型推断
- 对象类型:接口,类型别名,索引签名
- 对象的解构:基本解构,函数参数解构
- 对象的扩展运算符:扩展对象,剩余属性
- 对象的类型守卫:typeof 类型守卫,instanceof 类型守卫,in 操作符类型守卫,自定义类型守卫
- 对象的最佳实践:使用接口定义对象类型,使用可选属性和只读属性,使用解构简化代码,使用扩展运算符合并对象,使用类型守卫处理联合类型
- 常见错误:属性访问错误,类型错误,只读属性修改错误,this 指向错误,对象解构错误
- 实际应用场景:定义配置对象,处理 API 响应,实现状态管理,实现选项对象,实现工具函数
通过合理使用对象,你可以在 TypeScript 中构建更加模块化、可维护的代码,提高代码的可读性和可重用性。