This TypeScript error occurs when trying to assign an unknown type value directly to a string variable. TypeScript requires type narrowing or assertions to ensure type safety before assignment.
This error appears when you attempt to assign a value of type `unknown` to a variable or parameter that expects a `string` type. TypeScript introduced the `unknown` type as a type-safe counterpart to `any`. While `any` allows any operation without checks, `unknown` forces you to verify the type before using it. The `unknown` type represents values whose type is not known at compile time—commonly from user input, API responses, or third-party libraries. TypeScript's type system prevents direct assignment to more specific types to maintain type safety and prevent runtime errors. This is a deliberate design choice that encourages safer code by requiring explicit type checking before narrowing an `unknown` value to a specific type like `string`.
The safest approach is to check the type at runtime before assignment:
function processValue(value: unknown): string {
if (typeof value === 'string') {
return value; // TypeScript knows value is string here
}
throw new Error('Value is not a string');
}
// Or assign to variable
let data: unknown = getUserInput();
if (typeof data === 'string') {
const myString: string = data; // ✓ Works
}This pattern ensures type safety while satisfying TypeScript's requirements.
If you have external knowledge that the value is definitely a string, use a type assertion:
const response: unknown = await fetch('/api/data').then(r => r.json());
const message = response as string; // Type assertion
// Or with angle bracket syntax (not usable in .tsx files)
const message = <string>response;Warning: This bypasses TypeScript's safety checks. Use only when you're absolutely certain of the type, otherwise you risk runtime errors.
For complex validations, define a type guard function with a type predicate:
function isString(value: unknown): value is string {
return typeof value === 'string';
}
const data: unknown = getExternalData();
if (isString(data)) {
const str: string = data; // ✓ TypeScript knows data is string
console.log(str.toUpperCase());
}Type guards are reusable and make your code more maintainable.
Assertion functions throw errors if validation fails, automatically narrowing the type:
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error(`Expected string, got ${typeof value}`);
}
}
const data: unknown = parseInput();
assertIsString(data);
// After this point, TypeScript knows data is a string
const myString: string = data; // ✓ WorksThis approach combines runtime validation with type narrowing.
When working with unknown from sources like catch blocks, narrow to the expected type or provide defaults:
async function fetchMessage(): Promise<string> {
try {
const response = await fetch('/api/message');
const data: unknown = await response.json();
// Validate the shape
if (typeof data === 'object' && data !== null && 'message' in data) {
const obj = data as { message: unknown };
if (typeof obj.message === 'string') {
return obj.message;
}
}
throw new Error('Invalid response format');
} catch (error: unknown) {
// error is unknown in catch blocks
if (error instanceof Error) {
return `Error: ${error.message}`;
}
return 'Unknown error occurred';
}
}For API responses and complex data structures, use runtime validation libraries:
import { z } from 'zod';
const stringSchema = z.string();
function processData(data: unknown): string {
// parse() throws if validation fails
return stringSchema.parse(data);
}
// Or with safeParse for error handling
function safeProcessData(data: unknown): string {
const result = stringSchema.safeParse(data);
if (result.success) {
return result.data;
}
throw new Error(`Validation failed: ${result.error.message}`);
}Libraries like Zod, io-ts, or Yup provide robust runtime validation with TypeScript integration.
Why unknown instead of any?
TypeScript 3.0 introduced unknown as a safer alternative to any. While any opts out of type checking entirely, unknown requires you to prove the type before using it. This catches potential bugs at compile time rather than runtime.
Type narrowing in control flow
TypeScript's control flow analysis tracks type narrowing through your code. Once you verify a type with typeof, instanceof, or a type guard, TypeScript remembers this in subsequent code branches.
Common pattern with catch blocks
Since TypeScript 4.4, catch clause variables have type unknown by default (with useUnknownInCatchVariables enabled). Always check if the error is an Error instance before accessing properties:
try {
riskyOperation();
} catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message); // Safe
} else {
console.error('Unknown error:', String(error));
}
}Non-null assertion operator
The ! operator can bypass null/undefined checks but does NOT work for unknown. You must use type guards or assertions.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript