Reputation: 27
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);
});
Upvotes: 0
Views: 134
Reputation: 209042
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
Upvotes: 4
Reputation: 691893
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.
Upvotes: 1