const typesOfBeetles = ["insects", "musicians"];
typesOfBeetles.push("cars"); // ok
const typesOfBeetles = ["insects", "musicians"] as const;
// Property 'push' does not exist on type 'readonly ["insects", "musicians"]'.ts(2339)
typesOfBeetles.push("cars");
Without as const
:
With as const
:
Take this code:
const directions = ["north", "south", "east", "west"];
function move(direction: string) {
console.log(`You moved ${direction}`);
}
The move
function takes one argument: direction
. direction
has type string
right now, but really, its only options should be the values in the directions
array. How can we make that happen?
This doesn't work:
const directions = ["north", "south", "east", "west"];
type Direction = typeof directions; // string[]
We could use an indexed access type:
const directions = ["north", "south", "east", "west"];
type Direction = (typeof directions)[number]; // string
This gives us the type of the elements in the array instead of the type of the array itself. But this still doesn't work because TypeScript is inferring the type as string
. The problem is, we could mutate the array:
const directions = ["north", "south", "east", "west"];
directions.push("blah");
So TypeScript has to go with a broad type that accommodates us adding to the array. But if we use as const
:
const directions = ["north", "south", "east", "west"] as const;
directions.push("blah"); // error!
Now TypeScript will know that the array is immutable. Now, using typeof
gives us a more specific type:
type Direction = (typeof directions)[number]; // "north" | "south" | "east" | "west"
Perfect! Now we can use it:
const directions = ["north", "south", "east", "west"] as const;
type Direction = (typeof directions)[number];
function move(direction: Direction) {
console.log(`You moved ${direction}`);
}
move("north");
// Argument of type '"blah"' is not assignable to parameter of type '"north" | "south" | "east" | "west"'.ts(2345)
move("blah");