Resolving @typescript-eslint/no-unsafe-assignment Warnings: Strategies for Type-Safe API Response Handling

Dec 07, 2025 · Programming · 8 views · 7.8

Keywords: TypeScript | ESLint | Type Safety | Generics | API Integration

Abstract: This article provides an in-depth analysis of the common @typescript-eslint/no-unsafe-assignment warning in TypeScript projects, which occurs when assigning any-typed values to non-any variables. Through examination of a concrete code example, it explains the differences between TypeScript compiler and ESLint type checking, and focuses on leveraging TypeScript's type inference features (such as ReturnType, typeof, and property access) to avoid interface duplication. The article presents practical solutions for refactoring API call functions using generic parameters to ensure response data matches local state types, achieving full type safety while maintaining code conciseness.

In TypeScript development, ESLint's @typescript-eslint/no-unsafe-assignment rule often flags potential type safety issues, particularly when handling response data from external APIs. This article will analyze a typical scenario in detail, explain the causes of this warning, and provide an elegant solution that avoids code duplication while enhancing type safety.

Problem Context and Code Example

Consider the following Vue 3 Composition API code snippet that defines a reactive state and attempts to fetch user profile data from a Graph API:

const defaultState = () => {
  return {
    profile: {
      id: '',
      displayName: '',
      givenName: '',
    },
    photo: '',
  }
}

const state = reactive(defaultState())

export const setGraphProfile = async () => {
  const response = await getGraphProfile()
  state.profile = { ...defaultState().profile, ...response.data }
}

This code triggers the ESLint warning: @typescript-eslint/no-unsafe-assignment: Unsafe assignment of an any value. The root cause is that the getGraphProfile function returns Promise<AxiosResponse<any>>, making response.data of type any, while state.profile expects an object with a specific structure.

Type Checking Differences Between TypeScript and ESLint

A common confusion arises: why doesn't the TypeScript compiler report an error, while ESLint issues a warning? This is because TypeScript treats the any type as a "universal type," allowing assignment to any other type without compilation errors. While this design offers flexibility, it weakens type system safety. ESLint's @typescript-eslint/no-unsafe-assignment rule addresses this gap by forcing developers to explicitly handle assignments of any-typed values, preventing potential type mismatches.

Type Inference Techniques to Avoid Interface Duplication

The most straightforward solution is to define an interface for the API response, but this would duplicate the structure of the defaultState().profile object. TypeScript provides powerful type inference capabilities that elegantly solve this problem:

type IProfile = ReturnType<typeof defaultState>["profile"]

This line utilizes three key features:

Through this approach, the IProfile type automatically aligns with the structure of defaultState().profile, eliminating the need for manual maintenance of duplicate interface definitions.

Refactoring API Calls for Type Safety

To ensure API responses match local state types, refactoring should begin at the foundational callGraph function. By adding a generic parameter, the expected response data type can be specified:

function callGraph<T>(url: string, token: string, axiosConfig?: AxiosRequestConfig) {
  const params: AxiosRequestConfig = {
    method: 'GET',
    url: url,
    headers: { Authorization: `Bearer ${token}` },
  }
  return axios.request<T>({ ...params, ...axiosConfig })
}

Then, when calling callGraph, pass the IProfile type:

...
if (response && response.accessToken) {
  return callGraph<IProfile>(uri, response.accessToken, axiosConfig)
}
...

This ensures the entire call chain—from callGraph to getGraphDetails to getGraphProfile—returns correctly typed response data, completely eliminating the safety hazards associated with any types.

Supplementary Approaches and Best Practices

In addition to the above solution, consider these supplementary strategies:

  1. Define Types at the Lowest Level: As noted in Answer 2, placing type definitions at the callGraph function level ensures type consistency throughout the call chain.
  2. Leverage Axios Generic Support: Directly use the axios<T>() syntax to pass type parameters, a standard practice in many modern HTTP libraries.
  3. Runtime Type Validation: For uncertain external data sources, combine with libraries like Zod or io-ts for runtime type validation, providing dual assurance.

By comprehensively applying these techniques, developers can build highly type-safe applications without sacrificing code conciseness, effectively preventing runtime errors caused by type mismatches.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.