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.
as never
is very occasionally needed in TypeScript. Let's look at an example where it's necessary.
Let's imagine we want to format some input based on its typeof
. We first create a formatters
object that maps typeof
to a formatting function:
const formatters = {
string : (input : string) => input .toUpperCase (),
number : (input : number) => input .toFixed (2),
boolean : (input : boolean) => (input ? "true" : "false"),
};
Next, we create a format
function that takes an input of string | number | boolean
and formats it based on its typeof
.
const format = (input : string | number | boolean) => {
// We need to cast here because TypeScript isn't quite smart
// enough to know that `typeof input` can only be
// 'string' | 'number' | 'boolean'
const inputType = typeof input as
| "string"
| "number"
| "boolean";
const formatter = formatters [inputType ];
return formatter (input );Argument of type 'string | number | boolean' is not assignable to parameter of type 'never'.
Type 'string' is not assignable to type 'never'.2345Argument of type 'string | number | boolean' is not assignable to parameter of type 'never'.
Type 'string' is not assignable to type 'never'.};
But there's a strange error:
Type 'string' is not assignable to type 'never'.
What's going on here?
Let's take a deeper look at the type of formatter
inside our format
function:
const format = (input : string | number | boolean) => {
const inputType = typeof input as
| "string"
| "number"
| "boolean";
const formatter = formatters [inputType ];
return formatter (input );
};
As you can see, it resolves to a union of functions, each with a different parameter. One function takes a string
, another a number
, and the last a boolean
.
How could we possibly call this function with a string
and a number
at the same time? We can't.
So, the function actually resolves to:
type Func = (input : never) => string;
You might be thinking, "Shouldn't the parameters resolve to a union of string | number | boolean
?"
This doesn't work, because calling formatters.string
with a number
is unsafe. Calling formatters.boolean
with a number
is unsafe.
So, never
is the only type that makes sense.
We happen to know that the logic of this function is sound. We know that formatters[inputType]
will resolve to the correct type.
So, we can use an as never
:
const format = (input : string | number | boolean) => {
const inputType = typeof input as
| "string"
| "number"
| "boolean";
const formatter = formatters [inputType ];
return formatter (input as never);
};
This forces TypeScript to consider input
as the type of never
- which is, of course, assignable to formatter
's parameter of never
.
as any
Work?Amazingly, any
doesn't work here:
const format = (input : string | number | boolean) => {
const inputType = typeof input as
| "string"
| "number"
| "boolean";
const formatter = formatters [inputType ];
return formatter (input as any );Argument of type 'any' is not assignable to parameter of type 'never'.2345Argument of type 'any' is not assignable to parameter of type 'never'.};
It results in a horrendous error:
Argument of type 'any' is not assignable to parameter of type 'never'.
So, as never
is the only way to go here.
Share this article with your friends
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.