TypeScript进阶指南:泛型和装饰器的妙用
TypeScript作为JavaScript的超集,为大型应用的开发提供了强大的工具和概念,使得代码更加健壮和可维护。在TypeScript的众多高级特性中,泛型和装饰器是两个极其强大的工具。它们能够提高代码的复用性,同时为复杂的问题提供清晰、类型安全的解决方案。在这篇进阶指南中,我将深入探讨泛型和装饰器的高级用法,并通过具体的代码示例来展现它们如何提升我们代码的质量。
泛型的妙用
泛型是TypeScript中实现代码复用的一个重要工具。它允许我们编写可适用于多种类型的组件,而不必牺牲类型的安全性。
基础概念
泛型可以被认为是类型的变量,它可以捕获传递给组件的类型信息,并在整个组件中使用这些信息。
function identity<T>(arg: T): T {
return arg;}
在这个
identity
函数中,
T
是一个类型变量,它捕获了用户提供的类型(即传递给
identity
的参数的类型)。这样,这个函数就可以返回几乎任何类型的输入,同时保持类型信息。
高级用法:约束泛型
我们不仅可以定义泛型,还可以对泛型施加约束,以限制泛型可以表示的类型。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;}
在这个例子中,我们定义了一个
Lengthwise
接口,它有一个单独的
.length
属性。然后我们说,泛型
T
必须符合
Lengthwise
的形状,这意味着传递给
loggingIdentity
的参数必须有
.length
属性。
使用泛型的实际案例
假设我们正在编写一个前端应用程序,需要从服务器获取不同类型的资源。我们可以编写一个泛型函数,来处理对不同资源的GET请求。
async function getResource<T>(url: string): Promise<T> {
let response = await fetch(url);
let body = await response.json();
return body as T;
}
// 使用
interface User {
name: string;
email: string;
}
async function getUser(userId: string) {
const user = await getResource<User>(`/api/users/${userId}`);
console.log(user.name);}
在这个
getResource
函数中,我们使用了泛型
T
来捕获响应体的类型,这样我们就可以根据不同的调用安全地推断出返回类型。
装饰器的妙用
装饰器是TypeScript中用来修改类、方法、访问器、属性或参数的声明的特殊类型的声明。装饰器使用
@expression
这样的形式,
expression
求值后必须为一个函数,它会在运行时被调用。
类装饰器
类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视、修改或替换类定义。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}}
在这个例子中,
sealed
装饰器会封闭
Greeter
类,阻止添加新的属性,并且在实例化时标记为不可配置。
方法装饰器
方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。它会被应用到方法的属性描述符上,可以用来监视、修改或替换方法定义。
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}}
在这个例子中,
enumerable
装饰器修改了
greet
方法的枚举性。当我们设置为
false
时,这个方法就不会出现在类的枚举属性中。
装饰器工厂
如果我们想自定义装饰器如何应用到一个声明上,我们可以写一个装饰器工厂函数。装饰器工厂是一个简单的函数,它返回表达我们装饰器在运行时调用方式的函数。
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string;
const getter = function () {
return formatString.replace("%s", value);
};
const setter = function (newVal: string) {
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class Greeter {
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}}
在这个例子中,
format
装饰器工厂允许我们将一个字符串模板应用于
Greeter
类的
greeting
属性。每次访问这个属性时,都会返回一个格式化后的字符串。
结论
泛型和装饰器是TypeScript提供的强大工具,它们能够极大地提高我们代码的复用性和可维护性。通过泛型,我们可以编写出适用于多种类型的通用组件,而装饰器则允许我们以声明性的方式修改和增强类和类成员的行为。掌握了这些高级特性,我们就可以编写出更加灵活和健壮的TypeScript代码。
如果喜欢我的内容,不妨点赞关注,我们下次再见!
大家注意:因为微信最近又改了推送机制,经常有小伙伴说错过了之前被删的文章,或者一些限时福利,错过了就是错过了。所以建议大家加个 星标 ,就能第一时间收到推送。
点个喜欢支持我吧,点个 在看 就更好了