使用Typescript编写Angular 5单元测试,我有一个函数将查询DOM并返回MyComponent
类的实例(如果找到):
function getMyComponent(hostFixture: ComponentFixture<any>): MyComponent {
const debugElement = hostFixture.debugElement.query(By.directive(MyComponent));
return (debugElement && debugElement.componentInstance) || null;
}
这完美无瑕。现在我想概括一下这个函数,以便它可以按需查询任何类型。我认为这可以用泛型类型完成。函数将被调用如下:
const instance: MyComponent = getDirective<MyComponent>(fixture);
我不能让它工作;这是破碎的功能:
function getDirective<T>(hostFixture: ComponentFixture<any>): T {
const debugElement = hostFixture.debugElement.query(By.directive(T));
return (debugElement && debugElement.componentInstance) || null;
}
函数中的第一行会引发错误:
TS2693:'T'仅指类型,但在此处用作值
这里的语法是什么?我正在调用的Angular函数 - By.directive()
- 采用Type<any>
类型的参数,并且是documented here
答案 0 :(得分:5)
Angular的Type<T>
是一个糟糕的API。你是众多因其坦率不负责任的命名而误导的人之一(见评论)。 Type<T>
实际上意味着可构建。它是您可以使用new
调用的值的类型。
By.directive
取值而不是类型。该值的类型是可以使用new
调用的东西,可以构造。这意味着一个类或一个函数。
但是,你可以编写你想要的功能。
以下是我的写作方式。
function getDirective<T>(
hostFixture: ComponentFixture<any>,
ComponentConstructor: new (...args: any[]) => T
): T {
const debugElement = hostFixture.debugElement.query(By.directive(ComponentConstructor));
return debugElement && debugElement.componentInstance || undefined;
}
然后像这样称呼它
const instance = getDirective(fixture, MyComponent);
<强>说明:强>
TypeScript类型纯粹是设计时构造。该语言的整个类型系统经过精心设计,可在转换过程中完全擦除,尤其对程序的运行时行为没有影响。
为什么我说名字Type<T>
是不负责任的?因为成千上万的程序员在Angular的上下文中第一次学习TypeScript。通过命名类型Type,他们很容易误解。
这一点尤其重要,因为类型Type<T>
通常归因于一个类的值,许多人已经将类型与TypeScript和vanilla JavaScript中的类混淆。
Angular想要将这些概念混为一谈,希望 JavaScript中的类是类似于许多其他语言的类型,但它们根本不是。值得注意的是,我更喜欢的Aurelia这样的框架也鼓励这种混淆,但是,因为它们不会尝试来强制使用TypeScript,因为它们允许通过类上的属性指定依赖项(可能具有讽刺意味)在方式或AngularJS中,混淆的可能性较小。无论如何,TypeScript类型,ECMAScript类和依赖注入都是完全正交的概念。
鉴于TypeScript的性质,它只提供设计时类型,这会导致严重的混淆。
值得注意的是,Angular团队设计了他们自己的编程语言,专门用于开发Angular 2的@Script。@ Script是JavaScript和TypeScript的衍生物,它通过在避开时添加某种程度的类型来区分自己装饰器用于当时竞争的 Annotations 功能。他们在2014年末放弃了这种语言,而选择使用TypeScript。
我仍然有时认为启发@Script的相同哲学仍会影响Angular。