Dynamic Static Typing In TypeScript
JavaScript is an inherently dynamic programming language. We as developers can express a lot with little effort, and the language and its runtime figure out what we intended to do. This is what makes JavaScript so popular for beginners, and which makes experienced developers productive! There is a caveat, though: We need to be alert! Mistakes, typos, correct program behavior: A lot of that happens in our heads!
Take a look at the following example.
app.get("/api/users/:userID", function(req, res) {
if (req.method === "POST") {
res.status(20).send({
message: "Got you, user " + req.params.userId
});
}
})
We have an https://expressjs.com/-style server that allows us to define a route (or path), and executes a callback if the URL is requested.
The callback takes two arguments:
- The
requestobject. Here we get information on the HTTP method used (e.g GET, POST, PUT, DELETE), and additional parameters that come in. In this exampleuserIDshould be mapped to a parameteruserIDthat, well, contains the user’s ID! - The
responseorreplyobject. Here we want to prepare a proper response from the server to the client. We want to send correct status codes (methodstatus) and send JSON output over the wire.
What we see in this example is heavily simplified, but gives a good idea what we are up to. The example above is also riddled with errors! Have a look:
app.get("/api/users/:userID", function(req, res) {
if (req.method === "POST") { /* Error 1 */
res.status(20).send({ /* Error 2 */
message: "Welcome, user " + req.params.userId /* Error 3 */
});
}
})
Oh wow! Three lines of implementation code, and three errors? What has happened?
- The first error is nuanced. While we tell our app that we want to listen to GET requests (hence
app.get), we only do something if the request method is POST. At this particular point in our application,req.methodcan’t be POST. So we would never send any response, which might lead to unexpected timeouts. - Great that we explicitly send a status code!
20isn’t a valid status code, though. Clients might not understand what’s happening here. - This is the response we want to send back. We access the parsed arguments but have a mean typo. It’s
userIDnotuserId. All our users would be greeted with “Welcome, user undefined!”. Something you definitely have seen in the wild!
And things like that happen! Especially in JavaScript. We gain expressiveness – not once did we have to bother about types – but have to pay close attention to what we’re doing.
This is also where JavaScript gets a lot of backlash from programmers who aren’t used to dynamic programming languages. They usually have compilers pointing them to possible problems and catching errors upfront. They might come off as snooty when they frown upon the amount of extra work you have to do in your head to make sure everything works right. They might even tell you that JavaScript has no types. Which is not true.
Anders Hejlsberg, the lead architect of TypeScript, said in his MS Build 2017 keynote that “it’s not that JavaScript has no type system. There is just no way of formalizing it”.
And this is TypeScript’s main purpose. TypeScript wants to understand your JavaScript code better than you do. And where TypeScript can’t figure out what you mean, you can assist by providing extra type information.
Basic Typing
And this is what we’re going to do right now. Let’s take the get method from our Express-style server and add enough type information so we can exclude as many categories of errors as possible.
We start with some basic type information. We have an app object that points to a get function. The get function takes path, which is a string, and a callback.
const app = {
get, /* post, put, delete, ... to come! */
};
function get(path: string, callback: CallbackFn) {
// to be implemented --> not important right now
}
While string is a basic, so-called primitive type, CallbackFn is a compound type that we have to explicitly define.
CallbackFn is a function type that takes two arguments:
req, which is of typeServerRequestreplywhich is of typeServerReply
CallbackFn returns void.
type CallbackFn = (req: ServerRequest, reply: ServerReply) => void;
ServerRequest is a pretty complex object in most frameworks. We do a simplified version for demonstration purposes. We pass in a method string, for "GET", "POST", "PUT", "DELETE", etc. It also has a params record. Records are objects that associate a set of keys with a set of properties. For now, we want to allow for every string key to be mapped to a string property. We refactor this one later.
type ServerRequest = {
method: string;
params: Record<string, string>;
};
For ServerReply, we lay out some functions, knowing that a real ServerReply object has much more. A send function takes an optional argument with the data we want to send. And we have the possibility to set a status code with the status function.
type ServerReply = {
send: (obj?: any) => void;
status: (statusCode: number) => ServerReply;
};
That’s already something, and we can rule out a couple of errors:
app.get("/api/users/:userID", function(req, res) {
if(req.method === 2) {
// ^^^^^^^^^^^^^^^^^
Advertising by Adpathway





