Announcing: A Free Book, A New Course, A Huge Price Cut...
It's a massive ship day. We're launching a free TypeScript book, new course, giveaway, price cut, and sale.
Last week, the TypeScript team released TS 5.3.
As usual, I scanned the announcement post, but I quickly noticed something interesting.
One of the most important changes in TypeScript 5.3 wasn't mentioned in the release notes.
// This would error in 5.2, but is allowed in 5.3!
const array = ["a", "b", "c"] as const satisfies string[];
const returnWhatIPassIn = <const T extends any[]>(t : T ) => {
return t ;
};
// result is any[] in TS 5.2, but ['a', 'b', 'c'] in 5.3
const result = returnWhatIPassIn (["a", "b", "c"]);
Working with readonly arrays in TS can be occasionally a bit of a pain.
Let's say you want to declare an array of routes as const.
This allows you to reuse the paths declared for a type.
const arrayOfRoutes = [
{ path : "/home", component : Home },
{ path : "/about", component : About },
] as const ;
type Route = (typeof arrayOfRoutes )[number]["path"];
But what if you want to make sure that the arrayOfRoutes
conforms to a certain type?
For that, you can use satisfies.
const arrayOfRoutes = [
{ path : "/home", component : Home },
{ path : "/about", component : About },
] as const satisfies {
path : string;
component : React .FC ;
}[];
// Type is 'readonly' and cannot be
// assigned to a mutable type
The only trouble is that in TypeScript 5.2, this would give an error! But... Why?
Well, it's because arrayOfRoutes
is readonly, and you can't satisfy a mutable array with a readonly one.
So, the fix was to make the type we were satisfying a readonly
array:
const arrayOfRoutes = [
{ path : "/home", component : Home },
{ path : "/about", component : About },
] as const satisfies readonly {
path : string;
component : React .FC ;
}[];
// No more error!
Now that we're using a readonly array, TypeScript is happy.
The same was true when using const type parameters, but even more pernicious.
In this position, const
infers the thing passed into T
as if it were as const
.
But if you try to constrain it with an array, it doesn't work!
const returnWhatIPassIn = <const T extends any[]>(t : T ) => {
return t ;
};
// result is any[] in TS 5.2!
const result = returnWhatIPassIn (["a", "b", "c"]);
Before TS 5.3, the fix was to add readonly
to the type parameter:
const returnWhatIPassIn = <const T extends readonly any[]>(
t : T
) => {
return t ;
};
// result is ['a', 'b', 'c']!
const result = returnWhatIPassIn (["a", "b", "c"]);
But this fix was hard to find and required some deep knowledge of how const type parameters worked.
Since 5.3, TypeScript has relaxed the rules around readonly arrays.
In these two situations, TypeScript now acts more helpfully.
The satisfies
keyword now lets you pass in readonly arrays:
// This would error in 5.2, but is allowed in 5.3!
const array = ["a", "b", "c"] as const satisfies string[];
Const type parameters now infer the type passed in instead of defaulting to their constraints:
const returnWhatIPassIn = <const T extends any[]>(t : T ) => {
return t ;
};
// result is any[] in TS 5.2, but ['a', 'b', 'c'] in 5.3
const result = returnWhatIPassIn (["a", "b", "c"]);
Note that there is a small difference! If you were to specify readonly string[]
instead of string[]
, you would get a readonly array back.
So you still need to specify readonly if you want a readonly array back.
// This would error in 5.2, but is allowed in 5.3!
const array = [ "a",
"b",
"c",
] as const satisfies readonly string[];
const returnWhatIPassIn = <const T extends readonly any[]>(
t : T
) => {
return t ;
};
// result is any[] in TS 5.2, but ['a', 'b', 'c'] in 5.3
const result = returnWhatIPassIn (["a", "b", "c"]);
But this is a massive improvement, making both const
type parameters and satisfies
much easier to work with.
TypeScript - you gotta shout about these things! I'll be updating my course with this new behavior very soon.
Share this article with your friends
It's a massive ship day. We're launching a free TypeScript book, new course, giveaway, price cut, and sale.
Learn why the order you specify object properties in TypeScript matters and how it can affect type inference in your functions.
Learn how to use corepack
to configure package managers in Node.js projects, ensuring you always use the correct one.
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
Discover when it's appropriate to use TypeScript's any
type despite its risks. Learn about legitimate cases where any
is necessary.
Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.