How to fix Assertion signature required for type narrowing in TypeScript
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'
}- Add the 'asserts' keyword to your function signature
- Use 'asserts condition' for boolean conditions
- Throw an error when the condition fails
- Update validation library calls
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
- 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
- Trying to narrow a function parameter without explicit assertion syntax
- Using a third-party validation library that doesn't export assertion signatures
How to fix "Assertion signature required for type narrowing"
Change your function to use an assertion signature with the asserts keyword:
Before (doesn't work):
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):
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."
For general conditions that aren't about type narrowing, use asserts condition:
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.
Assertion functions must throw an error if the assertion fails. TypeScript interprets a successful return as proof the type is correct:
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.
If you're using a third-party validation library like Zod or Yup, wrap it with an assertion function:
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 UserAfter calling your assertion function, the type should be narrowed in 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 numberHover 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):
function isString(value: unknown): value is string {
return typeof value === 'string';
}
if (isString(data)) {
// data is string
}Assertion Functions (return void/asserts):
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') throw new Error();
}
assertIsString(data);
// data is stringUse 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:
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:
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.