Yusei
Yusei

Reputation: 27

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

Upvotes: 0

Views: 134

Answers (2)

Paul Samsotha
Paul Samsotha

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

JB Nizet
JB Nizet

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

Related Questions