Account imageLoginSign UpAccount image
Loading votes....
Save Question

What does the satisfies operator do in TypeScript 4.9+ and how is it different from a type annotation?

clock icon

asked 3 months ago

Message icon

1

Eye icon

3

I saw this new syntax:

1type Colors = 'red' | 'green' | 'blue';
2const color = 'red' satisfies Colors;
1type Colors = 'red' | 'green' | 'blue';
2const color = 'red' satisfies Colors;

Why not just write const color: Colors = 'red'? What problem does satisfies solve?

1 Answer

The problem satisfies solves

With a normal type annotation (: Colors), you lose the exact literal type – TypeScript widens it to the union. This causes issues when you need the literal for autocomplete or inference elsewhere.

typescript

1type Colors = 'red' | 'green' | 'blue';
2
3// ❌ Type annotation – widens to 'Colors', not literal
4const color1: Colors = 'red';
5// color1 is 'red' | 'green' | 'blue' (union, not literal)
6
7// ✅ satisfies – keeps literal 'red'
8const color2 = 'red' satisfies Colors;
9// color2 is 'red' (the literal)
1type Colors = 'red' | 'green' | 'blue';
2
3// ❌ Type annotation – widens to 'Colors', not literal
4const color1: Colors = 'red';
5// color1 is 'red' | 'green' | 'blue' (union, not literal)
6
7// ✅ satisfies – keeps literal 'red'
8const color2 = 'red' satisfies Colors;
9// color2 is 'red' (the literal)

Real‑world example: object properties

typescript

1const palette = {
2 primary: '#ff0000',
3 secondary: '#00ff00',
4 accent: 'blue' // typo – should be a hex code
5} satisfies Record<string, `#${string}`>;
6
7// The typo `'blue'` errors ✅ but palette still has its original shape
1const palette = {
2 primary: '#ff0000',
3 secondary: '#00ff00',
4 accent: 'blue' // typo – should be a hex code
5} satisfies Record<string, `#${string}`>;
6
7// The typo `'blue'` errors ✅ but palette still has its original shape

vs type annotation on object

typescript

1type Palette = Record<string, `#${string}`>;
2
3// ❌ Annotation loses all key names
4const p1: Palette = {
5 primary: '#ff0000' // p1.primary is string (no literal key info)
6};
7
8// ✅ satisfies keeps key names and checks values
9const p2 = {
10 primary: '#ff0000'
11} satisfies Palette;
12// p2.primary is '#ff0000' (literal), and autocomplete works
1type Palette = Record<string, `#${string}`>;
2
3// ❌ Annotation loses all key names
4const p1: Palette = {
5 primary: '#ff0000' // p1.primary is string (no literal key info)
6};
7
8// ✅ satisfies keeps key names and checks values
9const p2 = {
10 primary: '#ff0000'
11} satisfies Palette;
12// p2.primary is '#ff0000' (literal), and autocomplete works

Key differences

FeatureType annotation (: T)satisfies T
Enforces type
Preserves literal types❌ (widens)✅ (narrow)
Excess property checkyes (sometimes)yes
Keeps original inference
Works with as const❌ (overrides)✅ (adds on top)

Bonus: satisfies + as const

typescript

1const config = {
2 apiUrl: 'https://api.example.com',
3 timeout: 5000
4} as const satisfies { apiUrl: string; timeout: number };
5// Both `as const` (makes readonly) and `satisfies` (checks shape)
1const config = {
2 apiUrl: 'https://api.example.com',
3 timeout: 5000
4} as const satisfies { apiUrl: string; timeout: number };
5// Both `as const` (makes readonly) and `satisfies` (checks shape)

Use satisfies when: You want type checking without losing the most specific type (e.g., string literals, tuple lengths, object keys). It's perfect for validation of constants.

1

Write your answer here

Top Questions