Learn why TypeScript throws TS2352 when a type assertion seems incorrect, and how to safely convert between types.
TS2352 fires when you write a type assertion (as) between two types that TypeScript considers completely unrelated. TypeScript allows assertions between types that have some structural overlap, but when neither type extends the other and they share no common properties, TypeScript flags it as a likely mistake.
This is TypeScript protecting you from assertions that are almost certainly wrong. If you assert string as number, there's no runtime scenario where a string is actually a number — the assertion would just hide a bug.
// The general shape of the error:
// Conversion of type 'string' to type 'number' may be a mistake
// because neither type sufficiently overlaps with the other.Trying to assert one primitive type as another when there's no relationship between them.
// ❌ Broken
const userInput = "42"
const count = userInput as number
// ~~~~~~~~~~~~~~~~~~~~
// Error: Conversion of type 'string' to type 'number' may be a mistake
// because neither type sufficiently overlaps with the other.// ✅ Fixed — use a runtime conversion instead of an assertion
const userInput = "42"
const count = Number(userInput)When two interfaces have no shared properties, TypeScript won't allow a direct assertion.
// ❌ Broken
interface ShippingAddress {
street: string
city: string
zipCode: string
}
interface PaymentMethod {
cardNumber: string
expiryDate: string
cvv: string
}
const address: ShippingAddress = { street: "123 Main", city: "NYC", zipCode: "10001" }
const payment = address as PaymentMethod
// ~~~~~~~~~~~~~~~~~~~~~~~~
// Error: Conversion of type 'ShippingAddress' to type 'PaymentMethod'
// may be a mistake because neither type sufficiently overlaps.// ✅ Fixed — these are different types, map the data properly
const address: ShippingAddress = { street: "123 Main", city: "NYC", zipCode: "10001" }
const payment: PaymentMethod = {
cardNumber: "4111111111111111",
expiryDate: "12/28",
cvv: "123",
}A common pattern is asserting API response data to a specific type when TypeScript sees the response as a different shape.
// ❌ Broken
interface UserProfile {
id: number
displayName: string
avatarUrl: string
}
const response = await fetch("/api/profile")
const data: string = await response.text()
const profile = data as UserProfile
// ~~~~~~~~~~~~~~~~~~~~~~
// Error: Conversion of type 'string' to type 'UserProfile' may be a mistake.// ✅ Fixed — parse the JSON and validate the shape
interface UserProfile {
id: number
displayName: string
avatarUrl: string
}
const response = await fetch("/api/profile")
const profile = (await response.json()) as UserProfileUse runtime conversion functions. For primitives, use Number(), String(), Boolean(), or parseInt() instead of type assertions. These actually convert the value at runtime:
const age = Number("25") // actually converts to number
const text = String(42) // actually converts to stringFix the data flow. If the types genuinely don't overlap, you probably have a logic error. Check where the value comes from and ensure it's actually the type you need.
Use the double assertion pattern when justified. If you've validated the data yourself and need to bypass TypeScript's overlap check:
// Only use this when you've verified the data externally
const config = rawData as unknown as AppConfigAdd a type guard for runtime safety. Instead of blindly asserting, validate the shape of the data:
function isUserProfile(data: unknown): data is UserProfile {
return (
typeof data === "object" &&
data !== null &&
"id" in data &&
"displayName" in data
)
}
const data = await response.json()
if (isUserProfile(data)) {
// TypeScript now knows data is UserProfile
console.log(data.displayName)
}Check if you need as at all. Many assertions are unnecessary. If the type is correct, TypeScript will infer it. If it's wrong, the assertion is hiding a bug.
TS2352 occurs when a type assertion uses as to convert between two types that have no structural overlap. TypeScript allows assertions when one type is a supertype or subtype of the other (e.g., unknown as string or HTMLElement as HTMLInputElement), but blocks assertions between completely unrelated types. This check prevents you from writing assertions that could never be correct at runtime.
First, ask whether you need a type assertion at all. If you're converting between primitive types, use a conversion function like Number() or String(). If you're working with API data, parse it with JSON.parse() or response.json() and then assert or validate. If you've already validated the data and need to override TypeScript, use a double assertion through unknown: value as unknown as TargetType.
Double assertion (value as unknown as Type) is a deliberate escape hatch. It's acceptable in a few scenarios: when writing test mocks where you need to satisfy a type without implementing every property, when working with external data you've already validated through a type guard, or when bridging gaps in third-party library types. It should always be accompanied by a comment explaining why it's safe. If you find yourself using it frequently, the underlying types probably need redesigning.
Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.