How to design a Typescript Model for Response Returned By HttpClient library in Angular.
Angular 4.3 Introduced us to
HttpClient
a new API to handle Http Requests with ample of new feautres .
Note:
The Older HttpModule
is deprecated from Angular’s Version 4.3 so if you have not started using HttpClientModule
yet it’s high time for you to switch to it.
Among all the newly introduced mind-boggling features which can be found here we are going to demystify how to Type check responses
against your designed Model
Whats’s Type Checking in TypeScript means?
TypeScript is a strict syntactical superset of JavaScript and adds optional static typing
to the language
In Static typing, type checking is done at compile-time.
Type Checking means Once you declare a variable to be a certain type, it’s the compiler job to ensure that it is only ever assigned values of that type (or values that are sub-types of that type).
example:
here foo is declared as type string
and typescript throw a warning when a number is assigned to it.
Dead Simple ain’t it and in case if you want to dive in a bit deeper you can check this out.
next, let’s take a look at Typescript generics which makes it possible to type check the responses against your designed Model.
Generic types
In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.
https://www.typescriptlang.org/docs/handbook/generics.html
Here Component could be a method/class/interface.
Advantages of Generics:
- Code Reuse: We can write a method/class/interface once and use for any type we want.
- Type Safety: Generics make errors to appear at compile time than at runtime (It’s always better to know problems in your code at compile time rather than making your code fail at runtime).
Well, the definition is comprehensible yet bookish. In much simpler terms
Generics in TypeScript lets you parametrize type.
Let's work with an example to get a vivid picture.
The echo function is a function that will return back whatever is passed in Though it’s a contrived example, its good illustration for understanding generics.
Now the main question is how to type this function i.e. how to specify the return type and input argument type. After all, it’s typescript and it should be typed.
The first thing that would hit your mind is to type it as any
because there are no restrictions on the type of input argument and return type.
Can you find the caveat in the current approach???
if echo(1)
is called with a number it’s quite evident to the coder that it will return a number, But TypeScript does not know about the return type of echo(1)
because echo is typed as any
and moreover, typescript does not even complain when it is assigned to foo which is of type string
.
How to enforce Typescript to complain about types in this scenario?
Well, Here is where typescript generics come into the play.
Generics features let’s you create a placeholder for the types that will later be replaced by a type argument when the generic type is instantiated and used. The instantiation of a generic type with actual type arguments is called a parameterized type.
Let’s make echo function generic the syntax is pretty simple.
function echo<T>(arg:T):T
Angle brackets (<>
) next to function name makes the function generic.
The placeholder for the Type Involved is T(T is a common Convention ) with this Typescript knows that T is a placeholder for type information
The placeholder is used to declare the input argument (arg:T):T
and the return type :T
.
Typescript does not type check the input argument rather it takes the note of the input argument type when the function is called and when the execution is completed Typescript ensures that value returned from the function is of the same type as the type that was passed in.
In this case, when echo is called with a number as an input argument Typescript makes a note of it and when it sees that return type and input argument must be of the same type it throws a warning when the returned value is assigned to foo of type string
.
Type Checking Http Response:
let’s assume the above data returned from the Server.
The subscribe callback above requires bracket notation to extract the data values.
You can’t write result.id
because TypeScript correctly complains that the result
object from the service does not have a id
property.
Bracket notation
and dot notation
are functionally equivalent in JavaScript but are not the same thing in TypeScript. TypeScript is less strict with the bracket notation and this behavior is intentional to offer some kind of backdoor.
The HttpClient.get()
method parsed the JSON server response into the anonymous Object
type. It doesn't know what the shape of that object is.
You can tell HttpClient
the type of response to make consuming the output easier and more obvious.
Approach:
First, define an interface with the correct shape:
Revised get
method
Http
returns an Observable
and by type Checking, We can tell the HttpClient.get
to return response
as User
type When we use http.get<User>(…)
then it returns the instance of Observable<User>
type. PlaceHolder, in this case, is User
Now we can write result.id
because TypeScript is aware of the type of response.
Well, the next question is why to choose an interface over a class what is the advantage of using an interface over a class to design model?
A class is unsuitable for declaring a type that represents an
HTTP
response because the deserialized JSON values that result from HTTP requests will never be instances of a class. An interface is perfect candidate for it.
Class vs Interface
Classes and interfaces are powerful structures that facilitate not just object-oriented programming but also type-checking in TypeScript. A class is a blueprint from which we can create objects that share the same configuration properties and methods. An interface is a group of related properties and methods that describe an object, but neither provides implementation nor initialization for them.
Since both of these structures define what an object looks like, both can be used in TypeScript to type our variables. The decision to use a class or an interface truly depends on our use case: type-checking only, implementation details (typically via creating a new instance), or even both! We can use classes for type-checking and the underlying implementation
https://toddmotto.com/classes-vs-interfaces-in-typescript
Since we are interested only in type-checking the responses Interface is a good choice because the response text will be deserialized into plain JavaScript objects. Furthermore, an interface
is a virtual structure that only exists within the context of TypeScript. The TypeScript compiler uses interfaces solely for type-checking purposes. Once your code is transpiled
to its target language, it will be stripped from its interfaces - JavaScript isn’t typed, there’s no use for them there.
Since interfaces do not exist in runtime there is no runtime cost!