How To Strongly Type process.env
Learn how to strongly type process.env in TypeScript by either augmenting global type or validating it at runtime with t3-env.
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:
ts
constformatters = {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
.
ts
constformat = (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'constinputType = typeofinput as| "string"| "number"| "boolean";constformatter =formatters [inputType ];returnArgument 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'.formatter (); input };
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:
ts
constformat = (input : string | number | boolean) => {constinputType = typeofinput as| "string"| "number"| "boolean";constformatter =formatters [inputType ];returnformatter (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:
ts
typeFunc = (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
:
ts
constformat = (input : string | number | boolean) => {constinputType = typeofinput as| "string"| "number"| "boolean";constformatter =formatters [inputType ];returnformatter (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:
ts
constformat = (input : string | number | boolean) => {constinputType = typeofinput as| "string"| "number"| "boolean";constformatter =formatters [inputType ];returnArgument of type 'any' is not assignable to parameter of type 'never'.2345Argument of type 'any' is not assignable to parameter of type 'never'.formatter (input as any);};
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 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.
Improve React TypeScript performance by replacing type & with interface extends. Boost IDE and tsc speed significantly.
In this book teaser, we discuss deriving vs decoupling your types: when building relationships between your types or segregating them makes sense.
Learn how TypeScript's new utility type, NoInfer, can improve inference behavior by controlling where types are inferred in generic functions.