Literals

Literals are exact values that are JavaScript primitives.

String Literals

You can use a string literal as a type. For example:

  1. let foo: 'Hello';

Here we have created a variable called foo that will only allow the literal value 'Hello' to be assigned to it. This is demonstrated below:

  1. let foo: 'Hello';
  2. foo = 'Bar'; // Error: "Bar" is not assignable to type "Hello"

They are not very useful on their own but can be combined in a type union to create a powerful (and useful) abstraction e.g.:

  1. type CardinalDirection =
  2. "North"
  3. | "East"
  4. | "South"
  5. | "West";
  6. function move(distance: number, direction: CardinalDirection) {
  7. // ...
  8. }
  9. move(1,"North"); // Okay
  10. move(1,"Nurth"); // Error!

Other literal types

TypeScript also supports boolean, numbers as literals, e.g.:

  1. type OneToFive = 1 | 2 | 3 | 4 | 5;
  2. type Bools = true | false;

Inference

Quite commonly you get an error like Type string is not assignable to type "foo". The following example demonstrates this.

  1. function iTakeFoo(foo: 'foo') { }
  2. const test = {
  3. someProp: 'foo'
  4. };
  5. iTakeFoo(test.someProp); // Error: Argument of type string is not assignable to parameter of type 'foo'

This is because test is inferred to be of type {someProp: string}. The fix here is to use a simple type assertion to tell TypeScript the literal you want it to infer as shown below:

  1. function iTakeFoo(foo: 'foo') { }
  2. const test = {
  3. someProp: 'foo' as 'foo'
  4. };
  5. iTakeFoo(test.someProp); // Okay!

Use cases

Valid use cases for string literal types are:

String based enums

TypeScript enums are number based. You can use string literals with union types to mock a string based enum as we did in the CardinalDirection example above. You can even generate a Key:Value structure using the following function:

  1. /** Utility function to create a K:V from a list of strings */
  2. function strEnum<T extends string>(o: Array<T>): {[K in T]: K} {
  3. return o.reduce((res, key) => {
  4. res[key] = key;
  5. return res;
  6. }, Object.create(null));
  7. }

And then generate the literal type union using keyof typeof. Here is a complete example:

  1. /** Utility function to create a K:V from a list of strings */
  2. function strEnum<T extends string>(o: Array<T>): {[K in T]: K} {
  3. return o.reduce((res, key) => {
  4. res[key] = key;
  5. return res;
  6. }, Object.create(null));
  7. }
  8. /**
  9. * Sample create a string enum
  10. */
  11. /** Create a K:V */
  12. const Direction = strEnum([
  13. 'North',
  14. 'South',
  15. 'East',
  16. 'West'
  17. ])
  18. /** Create a Type */
  19. type Direction = keyof typeof Direction;
  20. /**
  21. * Sample using a string enum
  22. */
  23. let sample: Direction;
  24. sample = Direction.North; // Okay
  25. sample = 'North'; // Okay
  26. sample = 'AnythingElse'; // ERROR!

Modelling existing JavaScript APIs

E.g. CodeMirror editor has an option readOnly that can either be a boolean or the literal string "nocursor" (effective valid values true,false,"nocursor"). It can be declared as:

  1. readOnly: boolean | 'nocursor';

Discriminated Unions

We will cover this later in the book.