Yusei Yusei - 2 months ago 19
TypeScript Question

sample code: making mocks in angular2

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);
});

Answer

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

Comments