接口(Interfaces)类型
接口可以用来定义对象应该具有的属性和方法,而无需实现它们。下面是定义接口的基本语法:
interface InterfaceName {
// 属性定义
propertyName: Type;
// 可选属性
propertyName?: Type;
// 只读属性
readonly propertyName: Type;
// 方法定义
methodName(param1: Type, param2: Type): ReturnType;
}
下面是一些定义接口的例子:
简单接口
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
return "Hello, " + person.name;
}
let user: Person = {
name: "Alice",
age: 25
};
console.log(greet(user));
可选属性
在接口中,属性名后加上?
表示该属性是可选的:
interface Person {
name: string;
age?: number;
}
let user: Person = {
name: "Alice"
// age 属性是可选的,可以不提供
};
只读属性
在接口中,使用readonly
关键字定义只读属性,表示该属性在创建后不能被修改:
interface Person {
readonly id: number;
name: string;
}
let user: Person = {
id: 1,
name: "Alice"
};
// user.id = 2; // 错误,id 是只读属性
方法定义
接口也可以包含方法签名,但不提供具体实现:
interface Person {
name: string;
sayHello(): string;
}
let user: Person = {
name: "Alice",
sayHello: function() {
return `Hello, my name is ${this.name}`;
}
};
console.log(user.sayHello());
扩展接口
接口可以继承其他接口,使用extends
关键字:
interface Named {
name: string;
}
interface Greetable extends Named {
greet(phrase: string): void;
}
class Person implements Greetable {
name: string;
constructor(name: string) {
this.name = name;
}
greet(phrase: string) {
console.log(`${phrase}${this.name}`);
}
}
let user = new Person("Alice");
user.greet("Hello, I'm");
通过定义接口,TypeScript可以帮助你在编码时进行类型检查,确保对象符合特定的结构。这有助于在开发过程中捕获潜在的错误,并且使得代码更加清晰和易于维护。
泛型(Generics)
这种方法可以适用于多种类型而不是单一类型。泛型可以应用于函数、接口和类。以下是泛型的基本使用方法:
泛型函数
泛型函数可以处理多种类型的数据。下面是一个简单的泛型函数示例:
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("Hello, World!");
let output2 = identity<number>(100);
在这个例子中,T
是一个类型变量,它被用来捕获用户传入的类型(比如string
或number
),然后返回相同类型的值。
泛型接口
泛型也可以用于接口,以创建可复用的组件。下面是一个泛型接口的示例:
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
这里,GenericIdentityFn
是一个泛型接口,它描述了一个函数类型,这个函数接受一个类型为T
的参数并返回一个类型为T
的值。
泛型类
泛型类看起来类似于泛型接口。泛型类在实例部分可以使用类型变量。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 100));
console.log(stringNumeric.add(stringNumeric.zeroValue, "Test"));
在这个例子中,GenericNumber
类的类型变量T
在实例属性zeroValue
和add
方法中被使用。
泛型约束
有时候,你可能希望泛型函数能够处理多种类型,但又限制这些类型必须拥有某些共同的属性或方法。这时可以使用泛型约束:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在,我们知道 arg 有一个 .length 属性
return arg;
}
loggingIdentity({ length: 10, value: 3 });
// loggingIdentity(3); // 错误,数字没有 length 属性
在这个例子中,loggingIdentity
函数被约束为只能接收具有length
属性的参数。
多个类型参数
泛型函数可以有多个类型参数:
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
let result = swap<string, number>(["hello", 42]);
console.log(result); // [42, "hello"]
这里,swap
函数接受一个元组作为参数,并返回一个新的元组,其元素顺序被交换。
泛型是TypeScript中一个非常强大的特性,它提供了类型安全的同时保持了代码的灵活性。通过使用泛型,可以创建出既可复用又可适用于多种类型数据的组件。
类型断言(Type Assertions)
类型断言(Type Assertions)是一种告诉编译器你比它更了解某个值的类型的方式。类型断言类似于其他语言中的类型转换,但它不进行数据检查或重构,它只是在编译阶段告诉编译器“相信我,这个值就是这个类型”。
类型断言有两种形式:
1. “尖括号”语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
2. as
语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
这两种形式在功能上是等价的,但是as
语法在TypeScript中使用 JSX 时更受欢迎,因为尖括号在 JSX 中有特殊含义。
使用类型断言的注意事项:
- 类型断言不会在运行时对值进行检查,它不会改变值的类型,它只是告诉编译器如何处理该值。
- 类型断言不是类型转换,它不会触发任何特殊逻辑或类型检查。
- 在没有足够信息的情况下,不要过度使用类型断言,这可能会导致运行时错误。
- 当使用类型断言时,最好确保你的断言是正确的,否则可能会引入难以追踪的bug。
以下是一些使用类型断言的场景:
将联合类型断言为其中一个类型
interface Cat {
name: string;
purr(): void;
}
interface Dog {
name: string;
bark(): void;
}
function isCat(animal: Cat | Dog): animal is Cat {
return (animal as Cat).purr !== undefined;
}
function makeAnimalSound(animal: Cat | Dog) {
if (isCat(animal)) {
animal.purr();
} else {
animal.bark();
}
}
在这个例子中,isCat
函数使用了类型断言来检查一个动物是否是猫。
将any
类型断言为更具体的类型
当你从一个类型为any
的变量中读取数据时,你可能想断言一个更具体的类型,以便在之后的代码中使用。
let value: any = "Hello, TypeScript";
// 断言 value 是一个字符串
let message = value as string;
请记住,尽管类型断言很有用,但它应该谨慎使用,以避免隐藏潜在的运行时错误。
TS中处理异步操作
1. 使用回调函数
传统的JavaScript异步编程是通过回调函数来实现的。在TypeScript中,你可以通过定义函数参数为回调函数来处理异步操作。
function fetchData(callback: (data: any) => void) {
// 模拟异步操作,比如HTTP请求
setTimeout(() => {
callback({ message: 'Data fetched successfully' });
}, 2000);
}
fetchData((data) => {
console.log(data.message); // 输出: Data fetched successfully
});
2. 使用Promise
Promise
是ES6引入的,用于更优雅地处理异步操作。在TypeScript中,你可以使用Promise
来改进异步代码的可读性和可维护性。
function fetchData(): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ message: 'Data fetched successfully' });
}, 2000);
});
}
fetchData()
.then(data => {
console.log(data.message); // 输出: Data fetched successfully
})
.catch(error => {
console.error(error);
});
3. 使用async和await
async
和await
是ES2017引入的语法,它们提供了一种更简洁的方式来处理Promise
。在TypeScript中,你可以将函数声明为async
,并在函数内部使用await
来等待异步操作的结果。
async function fetchData(): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ message: 'Data fetched successfully' });
}, 2000);
});
}
async function run() {
try {
const data = await fetchData();
console.log(data.message); // 输出: Data fetched successfully
} catch (error) {
console.error(error);
}
}
run();
在这个例子中,fetchData
函数返回一个Promise
,而run
函数使用async
声明,并通过await
等待fetchData
的结果。使用try...catch
可以捕获异步操作中可能出现的错误。