Explained: 'React' refers to a UMD global
Find out why this error occurs and learn how to fix it.
The TypeScript docs are mostly excellent. The TypeScript handbook is a shining example of what good docs look like. They're precise, well-maintained, well laid-out, and complete.
However, if there's one section could us a rewrite, it would be the section on Generics. This section assumes too much knowledge, teaches things in the wrong order, and misses out key information. The generics section of the TypeScript docs leaves you feeling that generics are complicated and mysterious.
Let's rectify that.
Look at generics first on the type level. Imagine you have a type like this:
typescript
type Name = 'matt'
This is a literal string, matt
, expressed in a type alias. This can't be altered later, so it behaves similarly to a const
variable declaration in JavaScript:
typescript
const name = 'matt'
Using generics, we can change Name
to be a function. Add a type argument to it:
typescript
type Name<LastName> = 'matt'
This adds a parameter to Name
, meaning that whenever you use it, you'll need to pass in an argument:
ts
type User = { name: Name<'pocock'> }
This turns the type from a variable to a function. Now, its equivalent in JavaScript would be:
typescript
const name = (lastName) => 'matt'
Since it's now a function, let's name it something more function-like:
typescript
type GetName<LastName> = 'matt'
LastName
is currently unused, so as an example return an object from our type 'function':
typescript
type GetNameObject<LastName> = {firstName: 'matt'lastName: LastName}
The JavaScript equivalent would be:
typescript
const getNameObject = (lastName) => {return {firstName: 'matt',lastName,}}
You can then use our GetNameObject
type function to create types, by passing it arguments:
typescript
type NameObject = GetNameObject<'pocock'>// {// firstName: "matt";// lastName: "pocock";// }
In Total TypeScript's Type Transformations workshop this pattern is called 'type helpers'. Type helpers are really useful on the type level to create new types to help DRY up your code.
A common example is a Maybe
type helper:
typescript
type Maybe<T> = T | null | undefinedtype MaybeString = Maybe<string>// string | null | undefined
Now you know how to handle generics on the type level, what about generic functions?
Take a simple example, returnWhatIPassIn
:
typescript
const returnWhatIPassIn = (input: any) => {return input}
This function won't return whatever you pass in - it'll actually always return any
on the type level. This is annoying, because it ruins your autocomplete on the thing you pass in:
typescript
const str = returnWhatIPassIn('matt')str.touppercase() // No error here!
Imagine you wanted to create this on the type level. You'd create a type helper called ReturnWhatIPassIn
:
typescript
type ReturnWhatIPassIn<TInput> = TInputtype Str = ReturnWhatIPassIn<'matt'>// "matt"
At the type level, you're taking in TInput
- whatever it is - and returning it.
You can use a very similar syntax to annotate our function:
typescript
const returnWhatIPassIn = <TInput>(input: TInput): TInput => {return input}
This is now a generic function. This means that when you call it, the type argument of TInput
gets inferred from what you pass in:
typescript
const str = returnWhatIPassIn('matt')// "matt"
Put this in simple terms.
A generic function is a type helper layered on top of a normal function.
This means that when you call the function, you're also passing a type to the 'type helper'.
Build up that function again, piece by piece to make it clearer starting from your JavaScript-only function again:
typescript
const returnWhatIPassIn = (input: any) => {return input}
Get the structure of the 'type helper' set up first. You'll need to infer an TInput
argument, and return that.
typescript
const returnWhatIPassIn = <TInput>(input: any): TInput => {return input}
If you try and call this now, it'll infer what it returns as unknown
:
typescript
const str = returnWhatIPassIn('matt')// unknown
You haven't told the type helper which arguments to infer from. You need it to infer the type of TInput
from the input
argument. Fix that like this:
typescript
const returnWhatIPassIn = <TInput>(input: TInput): TInput => {return input}
So inside (input: TInput)
, you perform a mapping between the runtime argument - input
- and the type you want it to infer - TInput
.
This is the right way to think about generics - as a type helper laid over your function, with a mapping between them.
Generics get a lot more complex from here - multiple generics, generic constraints, generics hidden deep within other types - but this mental model stays the same.
Share this article with your friends
Find out why this error occurs and learn how to fix it.
Learn the differences between React.ReactNode
and JSX.Element
in TypeScript when working with React.
Since I first got into advanced TypeScript, I've been in love with a particular pattern. It formed the basis for one of my first-ever TypeScript tips, and it's been extraordinarily useful to me ever since. I call it the IIMT (rhymes with 'limped'): the Immediately Indexed Mapped Type.
There are three rules to keep in mind when deciding where to put types in your application code.
Discover the power of ComponentProps in React and TypeScript. Learn how to extract and utilize props from native HTML elements, existing components, and elements with associated refs. Enhance your development workflow with this essential type helper.
Testing types is a crucial aspect of developing libraries in TypeScript. In this article, we explore three different ways to test your types: using the vitest test runner, rolling your own solution with type helpers, and leveraging the tsd library.
The TypeScript 5.1 beta is out - here's everything you need to know.
There’s a difference between using TypeScript and knowing TypeScript.
The docs give you a good grasp of the pieces like generic functions, conditional types, and type helpers.
But out in the wild, developers are combining these pieces together into patterns.
Four of the most important patterns to know and use are:
Testing code doesn't need to be typed so strictly, and sometimes tests need to pass the wrong type. I made a library called shoehorn that eases the pain of working with tests in TypeScript by providing a first-class way to pass the wrong type to a function.
The article discusses why TypeScript does not throw an error when a function that is assigned to a variable doesn't match its type. It explains that a function with fewer parameters than its type can still be passed, and this behavior is not restricted to TypeScript but exists in JavaScript as well.
TypeScript 5.0 introduces const type parameters which are useful in preserving the literal types of objects passed to functions.
Updates to TypeScript 5.0 have made their way into Total TypeScript!
Exclude
is a very powerful utility type that can be used in a variety of ways. In this article, I'll show you 9 ways to use it along with code examples.
As a frontend developer, your job isn't just pixel-pushing. Most of the complexity in frontend comes from handling all the various states your app can be in.
It might be loading data, waiting for a form to be filled in, or sending a telemetry event - or all three at the same time.
If you aren't handling your states properly, you're likely to come unstuck. And handling states starts with how th
Using the satisfies keyword is one of four ways to make type assignments in TypeScript. In this article we'll look at examples of when each method should be used.
Understand why TypeScript throws complicated errors by learning how to read them. Errors mirror the structure of the code being compared and can be simplified by changing the way types are assigned.
Use Zod to validate unknown inputs in your app, whether it's a CLI or a public API, to ensure that data entering your app is safe. Zod can also be used for 'sort of' trusted inputs, like third-party services, to ensure that the data returned is what you expect.
TypeScript's template literal syntax allows developers to manipulate and transform strings in a powerful way. This can be extended using unions, infer, and recursion to handle more complex tasks.
Donny (kdy1 on GitHub) is rewriting TypeScript in Rust hoping to speed up tsc which is slow due to its TypeScript base. In this interview, he explains some of the financial and technical challenges they face with this open-source project.
Let's imagine you're creating a function which sums up an array of objects. Here's one, taken from the Excalidraw codebase:
const sum = <T>(array: readonly T[], mapper: (item: T) => number): number => array.reduce((acc, item) => acc + mapper(item), 0)
Let's look at the type definition. This function takes in:
readonly T[]