Explained: 'React' refers to a UMD global
Find out why this error occurs and learn how to fix it.
As part of my work as a TypeScript educator, I get asked a lot about the TypeScript utility types - especially about how to use them in application code. So, I'm starting a series on them - built around real-world examples.
Let's start with the Exclude
type helper.
typescript
type Fruit = 'apple' | 'banana' | 'orange'type Result = Exclude<Fruit, 'orange'> // 'apple' | 'banana'
We can use Exclude
to remove a single member of a union. The first argument represents the full union, and the second argument represents the member to remove.
Technically, the second argument can be any type - TypeScript won't warn you if you try to remove a member that doesn't exist. It'll just return the original union.
typescript
type Result = Exclude<Fruit, 'pear'> // 'apple' | 'banana' | 'orange'
typescript
type Event = 'click' | 'focus' | 'change' | 'abort'type ClickAndFocusEvent = Exclude<Event, 'change' | 'abort'> // 'click' | 'focus'
We can also use Exclude
to remove multiple members from a union. By passing a union to the second argument, we can remove multiple members at once.
Just like above, not all of those members need to exist in the original union:
typescript
type ClickAndFocusEvent = Exclude<Event, 'change' | 'abort' | 'blur'> // 'click' | 'focus'
typescript
type Event =| {type: 'click'}| {type: 'focus'}| {type: 'change'}
A discriminated union is a union, usually of objects, which have a common property that can be used to discriminate between them. In the example above, the type
property is used to discriminate between the different events.
We can extract a subset of the union by using Exclude
to remove all members that don't have a specific value for the type
property.
typescript
type ClickAndFocusEvent = Exclude<Event, {type: 'change'}> // { type: 'click' } | { type: 'focus' }
This works even if the members of the union have other properties attached to them.
typescript
type Event =| {type: 'click'x: numbery: number}| {type: 'focus'}| {type: 'change'value: string}type ClickAndFocusEvent = Exclude<Event, {type: 'click'}> // { type: 'focus' } | { type: 'change', value: string }
In the example above, the x
and y
properties don't need to be passed to Exclude
in order to remove the click
event.
typescript
type Event =| {type: 'click'}| {type: 'focus'}| {type: 'change'}| {type: 'abort'}type ClickAndFocusEvent = Exclude<Event, {type: 'change'} | {type: 'abort'}> // { type: 'click' } | { type: 'focus' }
You can also remove multiple members of a discriminated union by passing a union to the second argument. This can either be a union of the members of the union, or a union of the type
property:
typescript
type ClickAndFocusEvent = Exclude<Event, {type: 'change' | 'abort'}> // { type: 'click' } | { type: 'focus' }
typescript
type Routes =| {route: '/user'search: {id: string}}| {route: '/user/create'}| {route: '/user/edit'search: {id: string}}type RoutesWithoutSearch = Exclude<Routes,{search: any}> // { route: '/user/create' }
You don't need to include the 'discriminator' (in this case, route
) in the second argument to Exclude
. You can just pass the shape of the members you want to remove.
In the example above, we're removing all members of the Routes
union that have a search
property.
typescript
type PossibleTypes = 'admin' | 'user' | 0 | 1 | 2type StringTypes = Exclude<PossibleTypes, number>// ^? 'admin' | 'user'
Exclude also works on basic types. In the example above, we're removing all literals that match number
from the PossibleTypes
union.
This can be useful if you want to remove all strings from a union, or all numbers, or all booleans.
typescript
type ObjectKey = 'userId' | 'postId' | 'userName' | 'postName'type PostKey = Exclude<ObjectKey, `${string}${'user'}${string}`> // 'postName' | 'postId'
You can use Exclude
to remove all strings from a union that contain a specific substring.
In the example above, we're removing all strings that contain the substring user
from the ObjectKey
union.
We use a template literal to represent the string we want to remove - in this case, user
. We then use the ${string}
syntax to represent any string that comes before or after the substring we want to remove.
typescript
type ObjectKey = 'userId' | 'postId' | 'id' | 'userName' | 'postName'type NonIdKey = Exclude<ObjectKey, `${string}${'id' | 'Id'}${string}`> // 'postName' | 'userName'
You can also use Exclude
to remove all strings from a union that contain one of several possible substrings.
In the example above, we're removing all strings that contain either id
or Id
from the ObjectKey
union. By passing a union to the template literal, we can remove multiple substrings at once.
typescript
type ObjectKey = 'userId' | 'postId' | 'id' | 'userName' | 'postName'type NonNameKey = Exclude<ObjectKey, `${string}Name`> // 'userId' | 'postId' | 'id'
You can use Exclude
to remove all strings from a union that have a certain prefix or suffix.
In the example above, we're removing all strings that end with Name
from the ObjectKey
union.
Here, ${string}
is used to represent a string of any length that comes before the substring we want to remove.
To switch this over to match a certain prefix, we can move ${string}
to the end of the template literal instead.
Exclude
is a very powerful utility type that can be used in a variety of ways. If you have other examples of how you've used Exclude
, let me know on my TypeScript Wizards Discord!
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!
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.
Learn how to use TypeScript generics on the type level and with functions to improve code readability, type safety, and reduce repetitive code. Use "type helpers" to create new types and generic functions to pass in and return specific types.
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[]