Literals
TypeScript has some fun special cases for literals in
source code.
In part, a lot of the support is covered in type widening
and narrowing ( example:type-widening-and-narrowing ) and it's
worth covering that first.
A literal is a more concrete subtype of a collective type.
What this means is that "Hello World" is a string, but a
string is not "Hello World" inside the type system.
// This lets you declare APIs which use unions to say it
only accepts a particular literal:
const helloWorld = "Hello World";
let hiWorld = "Hi World"; // this is a string because it is let
// This function takes all strings
declare function allowsAnyString(arg: string);
allowsAnyString(helloWorld);
allowsAnyString(hiWorld);
// This function only accepts the string literal "Hello World"
declare function allowsOnlyHello(arg: "Hello World");
allowsOnlyHello(helloWorld);
allowsOnlyHello(hiWorld);
// See how it transforms `name: "Sabrina"` to `name: string`
even though it is defined as a constant. This is because
the name can still change any time:
declare function allowsFirstFiveNumbers(arg: 1 | 2 | 3 | 4 | 5);
allowsFirstFiveNumbers(1);
allowsFirstFiveNumbers(10);
let potentiallyAnyNumber = 3;
allowsFirstFiveNumbers(potentiallyAnyNumber);
// At first glance, this rule isn't applied to complex objects.
const myUser = {
name: "Sabrina",
};
// Because myUser's name property can change, TypeScript
cannot use the literal version in the type system. There
is a feature which will allow you to do this however.
myUser.name = "Cynthia";
// When "as const" is applied to the object, then it becomes
a object literal which doesn't change instead of a
mutable object which can.
const myUnchangingUser = {
name: "Fatma",
} as const;
// "as const" is a great tool for fixtured data, and places
where you treat code as literals inline. "as const" also
works with arrays:
myUnchangingUser.name = "Raîssa";
const exampleUsers = [{ name: "Brian" }, { name: "Fahrooq" }] as const;