How to fix Assertion signature required for type narrowing in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 8, 2025· 95 views

This error occurs when you try to use a function for type narrowing without declaring an assertion signature. TypeScript 3.7+ requires explicit 'asserts' syntax to recognize type narrowing behavior in custom functions.

Quick fix

Fixes Assertion signature required for type narrowing

function isString(value: unknown): boolean {
  return typeof value === 'string';
}

const data: unknown = 'hello';
if (isString(data)) {
  console.log(data); // Still typed as 'unknown'
}
  1. Add the 'asserts' keyword to your function signature
  2. Use 'asserts condition' for boolean conditions
  3. Throw an error when the condition fails
  4. Update validation library calls
typescript — error
function isString(value: unknown): boolean {
return typeof value === 'string';
}
const data: unknown = 'hello';
if (isString(data)) {
console.log(data); // Still typed as 'unknown'
}
Assertion signature required for type narrowing

What this error means

When you create a custom function to validate or narrow a type, TypeScript doesn't automatically understand that your function performs type narrowing. Unlike built-in TypeScript constructs like `typeof` checks or `instanceof`, custom functions need explicit instructions via an assertion signature. An assertion signature tells TypeScript: "If this function returns without throwing an error, the value has been narrowed to a specific type." Without this signature, TypeScript treats your validation function as a regular function that returns void or boolean, ignoring any type-narrowing intent. This limitation exists because TypeScript can't analyze the runtime behavior of arbitrary JavaScript functions—you must tell it what type narrowing you intend.

Typical symptoms

  • Type remains 'unknown' or 'any' after calling a validation function
  • TypeScript error: 'Assertion signature required' on a custom type guard
  • You have a working runtime validation function but TypeScript doesn't narrow the type
  • Type guards work in JavaScript but not for TypeScript static analysis
  • IDE shows the parameter type hasn't changed after validation

Common causes

  1. Using a regular function instead of declaring an assertion signature (most common)
  2. Function returns boolean but doesn't use 'asserts' keyword
  3. Type guard returns a boolean type predicate but isn't marked as an assertion function
  4. Trying to narrow a function parameter without explicit assertion syntax
  5. Using a third-party validation library that doesn't export assertion signatures

How to fix "Assertion signature required for type narrowing"

1Add the 'asserts' keyword to your function signature

Change your function to use an assertion signature with the asserts keyword:

Before (doesn't work):

typescript
function isString(value: unknown): boolean {
  return typeof value === 'string';
}

const data: unknown = 'hello';
if (isString(data)) {
  console.log(data); // Still typed as 'unknown'
}

After (works):

typescript
function isString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new Error('Not a string');
  }
}

const data: unknown = 'hello';
isString(data);
console.log(data); // Now typed as 'string'

The assertion signature asserts value is string tells TypeScript: "If this function completes, the parameter is definitely a string."

2Use 'asserts condition' for boolean conditions

For general conditions that aren't about type narrowing, use asserts condition:

typescript
function assertTrue(value: boolean, message?: string): asserts value {
  if (!value) {
    throw new Error(message || 'Assertion failed');
  }
}

function isValidNumber(n: number): asserts n is number {
  assertTrue(n > 0, 'Number must be positive');
}

let num: unknown = 42;
isValidNumber(num as number);
console.log(num); // Typed as 'number'

The asserts value form (without is) indicates the function just checks a condition and throws if false.

3Throw an error when the condition fails

Assertion functions must throw an error if the assertion fails. TypeScript interprets a successful return as proof the type is correct:

typescript
function isArray(value: unknown): asserts value is unknown[] {
  if (!Array.isArray(value)) {
    throw new TypeError(`Expected array, got ${typeof value}`);
  }
}

const data: unknown = [1, 2, 3];
isArray(data);
data.map(x => x * 2); // Valid: data is now typed as 'unknown[]'

If the function returns without throwing, TypeScript assumes the narrowing is successful. If it returns normally when the condition is false, you'll get runtime errors.

4Update validation library calls

If you're using a third-party validation library like Zod or Yup, wrap it with an assertion function:

typescript
import { z } from 'zod';

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
});

type User = z.infer<typeof userSchema>;

function assertIsUser(value: unknown): asserts value is User {
  try {
    userSchema.parse(value);
  } catch (error) {
    throw new Error(`Not a valid user: ${error}`);
  }
}

const userData: unknown = JSON.parse(jsonString);
assertIsUser(userData);
console.log(userData.name); // Valid: userData is typed as User
5Verify the type is correctly narrowed

After calling your assertion function, the type should be narrowed in TypeScript:

typescript
function isString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new Error('Not a string');
  }
}

function isNumber(value: unknown): asserts value is number {
  if (typeof value !== 'number') {
    throw new Error('Not a number');
  }
}

const mixed: unknown = 'test';

isString(mixed);
console.log(mixed.toUpperCase()); // Valid: mixed is string

// After this line, TypeScript knows mixed can't be a number:
const checkType = (x: number) => x + 1;
// checkType(mixed); // Error: mixed is string, not number

Hover over the variable in your IDE after the assertion call—the type should show the narrowed type, not 'unknown'.

Advanced notes

### Difference Between Type Predicates and Assertion Functions

TypeScript offers two ways to create custom type narrowing:

Type Predicates (return boolean):

typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

if (isString(data)) {
  // data is string
}

Assertion Functions (return void/asserts):

typescript
function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== 'string') throw new Error();
}

assertIsString(data);
// data is string

Use type predicates in conditionals (if, filter). Use assertion functions for runtime validations that should fail loudly.

### Combining with instanceof

For class instances, use asserts with instanceof:

typescript
class User {
  constructor(public name: string) {}
}

function assertIsUser(value: unknown): asserts value is User {
  if (!(value instanceof User)) {
    throw new Error('Not a User instance');
  }
}

const obj: unknown = new User('Alice');
assertIsUser(obj);
console.log(obj.name); // Valid

### Type Guards in Arrays

For filtering arrays, use type predicates instead:

typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

const mixed: (string | number)[] = ['a', 1, 'b', 2];
const strings = mixed.filter(isString);
// strings is string[]

Assertion functions narrowing persists after the function call across the scope, whereas type predicates only narrow within if blocks or after filtering.

### Performance Note

Assertion functions are checked at compile-time but the runtime checks (throw statements) always execute. Use them for critical validations. For performance-sensitive code at scale, consider lazy validation or caching results.

Frequently asked questions

What does "Assertion signature required for type narrowing" mean?
When you create a custom function to validate or narrow a type, TypeScript doesn't automatically understand that your function performs type narrowing. Unlike built-in TypeScript constructs like typeof checks or instanceof, custom functions need explicit instructions via an assertion signature. " Without this signature, TypeScript treats your validation function as a regular function that returns void or boolean, ignoring any type-narrowing intent.
How do I fix "Assertion signature required for type narrowing"?
This error occurs when you try to use a function for type narrowing without declaring an assertion signature. TypeScript 3.7+ requires explicit 'asserts' syntax to recognize type narrowing behavior in custom functions.
What causes "Assertion signature required for type narrowing"?
Using a regular function instead of declaring an assertion signature (most common) Function returns boolean but doesn't use 'asserts' keyword Type guard returns a boolean type predicate but isn't marked as an assertion function
Is the "Assertion signature required for type narrowing" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading