· Jake Worth

Inspecting TypeScript's inferred types


Here’s an assertion from the TypeScript handbook: “TypeScript knows the JavaScript language and will generate types for you in many cases.”

Reading between the lines, these two lines of code, one explicitly typed and one not, should be equivalent.

explicitlyTyped.ts
let greeting: string = 'hello';
implicitlyTyped.ts
let greeting = 'hello';

We can assume the second example should be implicitly typed as a string because TypeScript “knows.” The handbook sometimes refers to this as “Contextual typing.”

But what’s going on under the hood? One way to answer that question is by emitting the type declaration. Consider this file with no explicit typing:

implicitlyTyped.ts
let greeting = 'hello';
const fullName = (first: string, last: string) => first + ' ' + last;
const answer = 42;

We run it through the tsc compiler with the --declaration and --emitDeclarationOnly flags— output the declaration, and only the declaration):

npx tsc implicitlyTyped.ts --declaration --emitDeclarationOnly

Which produces this:

implicitlyTyped.d.ts
declare let greeting: string;
declare const fullName: (first: string, last: string) => string;
declare const answer = 42;

There are a couple of neat things going on here:

  • We used let, so greeting can be re-assigned. TypeScript typed it as a string.
  • fullName concatenates two strings, so TypeScript determined that fullName must return a string, and typed it.
  • answer is a constant const, so TypeScript used a 42 number literal.

The last example is instructive. With const, TypeScript can infer a literal type (42) instead of the wider number. That’s one more argument in favor of (almost) always using const: tighter inferred types.

There are at least two more methods for inspecting these types that will be familiar to most TypeScript devs. The first is hovering on a variable in your TypeScript-server-enabled text editor. The second is throwing a type error and reasoning through it.