const assertions

TypeScript 3.4 introduces a new construct for literal values called const assertions.Its syntax is a type assertion with const in place of the type name (e.g. 123 as const).When we construct new literal expressions with const assertions, we can signal to the language that

  • no literal types in that expression should be widened (e.g. no going from "hello" to string)
  • object literals get readonly properties
  • array literals become readonly tuples
  1. // Type '"hello"'
  2. let x = "hello" as const;
  3. // Type 'readonly [10, 20]'
  4. let y = [10, 20] as const;
  5. // Type '{ readonly text: "hello" }'
  6. let z = { text: "hello" } as const;

Outside of .tsx files, the angle bracket assertion syntax can also be used.

  1. // Type '"hello"'
  2. let x = <const>"hello";
  3. // Type 'readonly [10, 20]'
  4. let y = <const>[10, 20];
  5. // Type '{ readonly text: "hello" }'
  6. let z = <const>{ text: "hello" };

This feature means that types that would otherwise be used just to hint immutability to the compiler can often be omitted.

  1. // Works with no types referenced or declared.
  2. // We only needed a single const assertion.
  3. function getShapes() {
  4. let result = [
  5. { kind: "circle", radius: 100, },
  6. { kind: "square", sideLength: 50, },
  7. ] as const;
  8. return result;
  9. }
  10. for (const shape of getShapes()) {
  11. // Narrows perfectly!
  12. if (shape.kind === "circle") {
  13. console.log("Circle radius", shape.radius);
  14. }
  15. else {
  16. console.log("Square side length", shape.sideLength);
  17. }
  18. }

Notice the above needed no type annotations.The const assertion allowed TypeScript to take the most specific type of the expression.

This can even be used to enable enum-like patterns in plain JavaScript code if you choose not to use TypeScript’s enum construct.

  1. export const Colors = {
  2. red: "RED",
  3. blue: "BLUE",
  4. green: "GREEN",
  5. } as const;
  6. // or use an 'export default'
  7. export default {
  8. red: "RED",
  9. blue: "BLUE",
  10. green: "GREEN",
  11. } as const;

Caveats

One thing to note is that const assertions can only be applied immediately on simple literal expressions.

  1. // Error! A 'const' assertion can only be applied to a
  2. // to a string, number, boolean, array, or object literal.
  3. let a = (Math.random() < 0.5 ? 0 : 1) as const;
  4. // Works!
  5. let b = Math.random() < 0.5 ?
  6. 0 as const :
  7. 1 as const;

Another thing to keep in mind is that const contexts don’t immediately convert an expression to be fully immutable.

  1. let arr = [1, 2, 3, 4];
  2. let foo = {
  3. name: "foo",
  4. contents: arr,
  5. } as const;
  6. foo.name = "bar"; // error!
  7. foo.contents = []; // error!
  8. foo.contents.push(5); // ...works!

For more details, you can check out the respective pull request.