This Crazy Syntax Lets You Get An Array Element's Type
Learn how to extract the type of an array element in TypeScript using the powerful Array[number]
trick.
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.
The TypeScript 5.3 Feature They Didn't Tell You About
Learn how to extract the type of an array element in TypeScript using the powerful Array[number]
trick.
Learn how to publish a package to npm with a complete setup including, TypeScript, Prettier, Vitest, GitHub Actions, and versioning with Changesets.
Enums in TypeScript can be confusing, with differences between numeric and string enums causing unexpected behaviors.
Is TypeScript just a linter? No, but yes.
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.