Stringly and Strongly typed TypeScript
Tags: typescript, beginners, guide
Published: 2021-04-06 17:33:55

---
title: Stringly and Strongly typed TypeScript
published: true
date: 2019-11-26 00:00:00 UTC
tags: typescript, beginner, guide
canonical_url: https://gal.hagever.com/posts/strongly-stringly-types-in-typescript/
---

There’s a rational fear coming from JavaScript to TypeScript. The thought of having to mess with static types instead of focusing on runtime code sounds annoying. For coders who know static typings from languages with inferior type systems, like Java, or languages with awesome, yet different type systems, like Reason, OCaml, Rust and Swift (which are all examples to the [Hindley–Milner type system](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system)), static typings might sound very off from the standard way of JavaScript-ing.

In turns out, that TypeScript can express very complicated types, thanks to its conditional types, literals and generics. In this short post we’re going to explore how to use typed string literals to have a safe interfaces to objects. This approach is the underlying mechanism of wrapping libraries like [`lodash`](https://github.com/lodash/lodash), [`ramda`](https://github.com/ramda/ramda) and my recent [`@soundtype/commander`](https://github.com/Schniz/soundtype-commander) and [`@soundtype/eventemitter`](https://github.com/Schniz/soundtype-eventemitter), all in a type safe manner.

## Stringly-typed[<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#stringly-typed)

Consider the following JavaScript function:

```javascript
function prop(obj, name) {
  return obj[name];
}

// Usage:

const user = { firstName: "Gal", lastName: "Schlezinger" };
const firstName = prop(user, "firstName");
```

The `prop` function above looks like something we can’t type in a static type system. It takes an object, and then a string, which is the property name, and returns an object. A naive implementation in TypeScript would be probably something like:

```typescript
function prop(obj: object, name: string): any {
  return obj[name];
}
```

This is, of course, a very unsafe function. How can we make it better? Let’s look at a simpler function, and try to type it first. We’ll talk about the `identity` function, that gets an object and returns the same object. Or in JavaScript:

```javascript
function identity(x) {
  return x;
}
```

Such a simple function! How would we write it in TypeScript? The naive approach, again, would look something like:

```typescript
function identity(x: any): any {
  return x;
}
```

But now, once we use `identity(10)`, we get the `any` type back. This is not what we want. To ensure we get the same type as we provided, we need to make the function _generic_. Now, whenever we call the `identity` function, TypeScript will also provide the type of the argument, whether by inference or explicitly by usage. This is how it looks like:

```typescript
function identity<T>(x: T): T {
  return x;
}

// by inference
const inferred = identity(10); // number

// explicitly
const explicit = identity<number>(10); // number
```

So, in conclusion, generics can help us leverage the underlying types whenever for type safety.

How do we use generics in a function like `prop`? Let’s start by adding a generic to the object we want to take data from:

```typescript
// This won't compile in strict mode!

function prop<T>(obj: T, name: string): any {
  return obj[name];
}
```

This implementation won’t compile, because we can’t index an unknown object (just a generic `T`) over a string. Maybe that’s an array? Also, we don’t know what the result type might be, so this is not good enough. A good utility TypeScript provides us is the way to ask the type of the keys of an object. This is done by using [`keyof Type`](https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types), so `keyof { hello: number, world: string }` would result in the `'hello' | 'world'` type, which is a union of 2 string literals.

We can leverage this by using it instead of asking for a `string`:

```typescript
function prop<T>(obj: T, name: keyof T): any {
  return obj[name];
}
```

Now, our function compiles, but we still don’t know what type we will get in return, and as we said before, returning an `any` is not acceptable.

The cool trick we can do, is to add _another_ generic, that will extend the `keyof T`, let’s call it `Key`. Then, we can access the return type by returning `T[Key]`:

```typescript
function prop<T, Key extends keyof T>(obj: T, name: Key): T[Key] {
  return obj[name];
}
```

Now, the function is truly generic, and will work as expected:

```typescript
prop({ hello: "world", year: 2019 }, "year"); // number
prop({ hello: "world", year: 2019 }, "hello"); // string
prop({ hello: "world", year: 2019 }, "missing"); // compilation error
```

This assures us we won’t have any runtime `undefined`s, by letting our compiler be smarter and look for mistakes we might do. This tiny example can be used all over the place to provide great developer experience for library users.

The TypeScript type system also allows recursive types, conditional types and much more awesome shenanigans, we might explore in the future.