问题
I am learning Angular2. In DI pages, there is sample code for mocking. https://angular.io/docs/ts/latest/guide/dependency-injection.html
What does it mean
let mockService = <HeroService> {getHeroes: () => expectedHeroes }
It looks like define mockService function from HeroService function.
What is <HeroService>? Is <HeroService> casting?
let expectedHeroes = [{name: 'A'}, {name: 'B'}]
let mockService = <HeroService> {getHeroes: () => expectedHeroes }
it('should have heroes when HeroListComponent created', () => {
let hlc = new HeroListComponent(mockService);
expect(hlc.heroes.length).toEqual(expectedHeroes.length);
});
回答1:
To add to JB Nizet's answer and to give a little explanation on the reasoning behind the code.
TypeScript uses Structural Type System1. What this means is that if it quacks like a duck, then it can be considered a duck (or more precisely, be compatible with a duck). Take for example
class Duck {
quack() { }
}
let duck = {
quack: () => {}
}
Since duck has a quack method, you can pass it to anything that expects a Duck, like
function doQuack(duck: Duck) {
duck.quack();
}
doQuack(duck);
TypeScript is smart enough to know that the duck object literal can be considered a Duck even if we never actually create an instance of a Duck using duck = new Duck(). This is because the structure of duck is enough to be compatible with the Duck type, because it matches the structure; the structure being only a single quack method.
If we were to try to type duck as Duck, and we didn't have the quack method, then we would get a compile error.
let duck: Duck = { // compile error
mooo: () => {}
};
let duck: Duck = {
quack: () => {} // OK
}
That being said, with your example, the HeroSerivce has two methods, one to get all the heroes, and one to get a hero by id.
class HeroService {
getHeroes(): Hero[] { .. }
getHeroById(id: number): Hero { .. }
}
And a HeroComponent with a constructor that accepts a HeroService
class HeroComponent {
constructor(heroService: HeroService) {}
}
Now if we try to pass the following
let mockService = { getHeroes: () => expectedHeroes }
to the HeroComponent constructor, we will get a compile error because the mockService doesn't match the structure of a HeroService. It only has the one getHeroes method, when the structure actually consists of two methods, getHeroes and getHero.
So to force the compiler to just accept it, we "cast" it to <HeroService>.
We could pass the following (without "casting") and it would work, because it matches the structure.
let mockService = {
getHeroes: () => expectedHeroes,
getHero: (id: number) => null
};
1 - Read more from TypeScript documentation chapter Type Compatibility
回答2:
In JavaScript and TypeScript, {a: b} is an object literal. It defined an object with one property a having the value b.
So
{getHeroes: () => expectedHeroes }
is an object with one property named getHeroes, whose value is () => expectedHeroes() => expectedHeroes. The value is thus a function taking no argument (()) and returning the value expectedHeroes.
<HeroService> is called a type assertion:
Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.
Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler.
来源:https://stackoverflow.com/questions/39813955/sample-code-making-mocks-in-angular2