
TypeScript Guide EP09: Partial, Required and NonNullable
Video
Watch the full video on YouTube:
Introduction
In this ninth chapter of our TypeScript series, we’ll explore three fundamental utility types: Partial, Required, and NonNullable. These types allow us to transform existing types safely, making them more flexible or more strict according to our needs. They’re especially useful when working with data modeling, configuration creation, or encapsulating different types of configurations.
Since we’ve covered function typing, assertions, and basic generics usage, we can finally introduce these three basic utility types.
Partial
Partial is a type that lets us take all attributes of a type and make them optional. When I say optional, I mean each attribute will be either its natural type or undefined.
Basic Example
Let’s create a pet type with ID, name, owner, and years:
// models/pet.ts - Base pet type
type Pet = {
id: number
name: string
owner: string
years: number
}
Now suppose we have a function to update this pet, but we only want to update partial data:
// partial.ts - Using Partial for partial updates
import { Pet } from './models/pet.js'
// Partial converts all properties to optional
const updatePet = (pet: Partial<Pet>): void => {
// We can update only the properties we need
}
// Only pass the name to update
updatePet({ name: "New name" })
If we inspect each attribute of our pet, we can see each has a question mark, preserving its type but also with undefined applied through a union.
Combining Partial with Omit
Normally our update functions don’t request the ID in the same data object. Let’s combine Partial with Omit:
// Combining Partial with Omit
const updatePet = (pet: Omit<Partial<Pet>, 'id'>): void => {
// All data is optional except the ID
}
// The ID doesn't come in the object
updatePet({ name: "New name" }) // Valid
We can also do the inverse, first omit the ID then apply partial:
// Inverse order: first Omit, then Partial
const updatePet = (Partial<Omit<Pet, 'id'>>): void => {
// Same result: all optional without ID
}
Use Case: Configurations
Partial is especially useful for configuration functions where the user only wants to configure some elements:
// Configuration with default values
type Config = {
host: string
port: number
dns: string
}
// Function receives partial configuration
const configure = (config: Partial<Config>): void => {
// Internally we have default values
}
// Only configure the host
configure({ host: "localhost" })
Required
Required does the complete opposite of Partial. Required lets you take an object with optional values and make absolutely all attributes required.
Basic Example
Let’s create a type with optional properties and transform it:
// models/pet.ts - Pet with optional owner
type Pet = {
id: number
name: string
owner?: string // Optional
years: number
}
// Required converts all properties to required
type MandatoryAttributes = Required<Pet>
// Now owner is required, without undefined
By doing this, every attribute that Pet has is now required. Owner no longer has undefined.
Use Case: Configuration with Default Values
Required is especially useful when developing an SDK or library:
// SDK Configuration
type Config = {
host?: string
port?: number
dns?: string
}
// Internal default values
const defaultConfig: Required<Config> = {
host: "localhost",
port: 5432,
dns: "my-dns"
}
// Function that resolves external config with internal
const setupSDK = (options: Partial<Config>): Required<Config> => {
return { ...defaultConfig, ...options }
}
// User can overwrite values
const configured = setupSDK({ host: "192.168.1.1" })
// configured has all required values
This is a very common practical example when creating libraries. We create a configuration stored internally with default values, and give the user the ability to overwrite those values with what they need.
NonNullable
NonNullable interacts with types that are union types. Its responsibility is to remove null and undefined types from our unions.
Basic Example
// Type that can be null or undefined
type EmailString = string | null | undefined
// NonNullable removes null and undefined
type NonNullableEmailString = NonNullable<EmailString>
// Only remains: string
Example with Literals
Let’s see a more extensive example with union literals:
// Animal species type
type AnimalSpecies =
| "mammals"
| "amphibians"
| "reptiles"
| "birds"
| "fishes"
| null // Unrecognized species
// NonNullable removes null
type ValidAnimalSpecies = NonNullable<AnimalSpecies>
// Only remains: "mammals" | "amphibians" | "reptiles" | "birds" | "fishes"
If we add undefined, NonNullable also removes it, regardless of where we put it.
Creating Derived Types with Indexing
NonNullable is especially useful when working with creating new objects from others and accessing attributes in an indexed way:
// Pet with optional owner
type Pet = {
id: number
name: string
owner?: string
years: number
}
// Create derived type with required owner and no undefined
type MyPetType = Omit<Pet, 'owner'> & {
owner: NonNullable<Pet['owner']>
}
// owner is string (without undefined)
We access the type directly from the object using bracket syntax, as if it were a JSON and we try to access by key.
Best Practices
- Use Partial whenever you want to make all attributes of an object optional
- Use Required when you have an object with optional attributes and want all to be required
- Use NonNullable whenever you want to remove
nullorundefinedfrom your types - Combine Partial with Omit to create safer update functions
- Use Required for configurations with default values in SDKs and libraries
- Access property types with
Type['propertyName']syntax to create derived types
Conclusion
Partial, Required, and NonNullable are fundamental tools for transforming types in TypeScript. Partial lets us create flexible versions of our types, Required guarantees completeness, and NonNullable eliminates the ambiguity of null values. Together, these utility types give us precise control over the shape and strictness of our types, improving the safety and clarity of our code.
Repository
💻 Repository: https://github.com/radagva/ts-example
