暮年|细数这些年被困扰过的 TS 问题( 三 )


比如在 ionic-native 项目中 , 它使用 Plugin 装饰器来定义 IonicNative 中 Device 插件的相关信息:
@Plugin({pluginName: 'Device',plugin: 'cordova-plugin-device',pluginRef: 'device',repo: '',platforms: ['Android', 'Browser', 'iOS', 'macOS', 'Windows'],})@Injectable()export class Device extends IonicNativePlugin {}在以上代码中 Plugin 函数被称为装饰器工厂 , 调用该函数之后会返回类装饰器 , 用于装饰 Device 类 。 Plugin 工厂函数的定义如下:
// export function Plugin(config: PluginConfig): ClassDecorator {return function(cls: any) {// 把config对象中属性 , 作为静态属性添加到cls类上for (let prop in config) {cls[prop] = config[prop];}cls['installed'] = function(printWarning?: boolean) {return !!getPlugin(config.pluginRef);};// 省略其他内容return cls;};}通过观察 Plugin 工厂函数的方法签名 , 我们可以知道调用该函数之后会返回ClassDecorator 类型的对象 , 其中 ClassDecorator 类型的声明如下所示:
declare type ClassDecorator = (target: TFunction)=> TFunction | void;类装饰器顾名思义 , 就是用来装饰类的 。 它接收一个参数 —— target: TFunction , 表示被装饰器的类 。 介绍完上述内容之后 , 我们来看另一个问题 @Plugin({...}) 中的 @ 符号有什么用?
其实 @Plugin({...}) 中的 @ 符号只是语法糖 , 为什么说是语法糖呢?这里我们来看一下编译生成的 ES5 代码:
var __decorate = (thisif (typeof Reflect === "object"else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 };var Device = /** @class */ (function (_super) {__extends(Device, _super);function Device() {return _super !== null}Device = __decorate([Plugin({pluginName: 'Device',plugin: 'cordova-plugin-device',pluginRef: 'device',repo: '',platforms: ['Android', 'Browser', 'iOS', 'macOS', 'Windows'],}),Injectable()], Device);return Device;}(IonicNativePlugin));通过生成的代码可知 , @Plugin({...}) 和 @Injectable() 最终会被转换成普通的方法调用 , 它们的调用结果最终会以数组的形式作为参数传递给 __decorate 函数 , 而在 __decorate 函数内部会以 Device 类作为参数调用各自的类型装饰器 , 从而扩展对应的功能 。
此外 , 如果你有使用过 Angular , 相信你对以下代码并不会陌生 。
const API_URL = new InjectionToken('apiUrl');@Injectable()export class HttpService {constructor(private httpClient: HttpClient,@Inject(API_URL) private apiUrl: string) {}}在 Injectable 类装饰器修饰的 HttpService 类中 , 我们通过构造注入的方式注入了用于处理 HTTP 请求的 HttpClient 依赖对象 。 而通过 Inject 参数装饰器注入了 API_URL 对应的对象 , 这种方式我们称之为依赖注入(Dependency Injection) 。
关于什么是依赖注入 , 在 TS 中如何实现依赖注入功能 , 出于篇幅考虑 , 这里阿宝哥就不继续展开了 。 感兴趣的小伙伴可以阅读 “了不起的 IoC 与 DI” 这篇文章 。
继续阅读:觉得装饰器有点难?那就进来看看呗
五、如何理解函数重载的作用5.1 可爱又可恨的联合类型由于 JavaScript 是一个动态语言 , 我们通常会使用不同类型的参数来调用同一个函数 , 该函数会根据不同的参数而返回不同的类型的调用结果:
function add(x, y) {return x + y;}add(1, 2); // 3add("1", "2"); //"12"