簡體   English   中英

@typescript-eslint/no-unsafe-assignment:任何值的不安全賦值

[英]@typescript-eslint/no-unsafe-assignment: Unsafe assignment of an any value

考慮以下代碼:

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 }
}

這會生成 ESLint 警告:

@typescript-eslint/no-unsafe-assignment:任何值的不安全賦值。

這意味着response.data中的屬性可能與profile中的屬性不匹配。 getGraphProfile的返回是Promise<AxiosResponse<any>> 當然,通過簡單地忽略它很容易擺脫這個 ESLint 警告:

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
state.profile = { ...defaultState().profile, ...response.data }

問題:

  • 如何塑造 Promise getGraphProfile中的數據使其匹配? 因為可以創建一個 TS interface ,但這只會使用 object defaultState().profile創建重復的代碼
  • 為什么 TypeScript 對此代碼沒有問題,但 linter 有問題? 兩者都不需要對齊嗎?

實現:

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

const getGraphDetails = async (
  uri: string,
  scopes: string[],
  axiosConfig?: AxiosRequestConfig
) => {
  try {
    const response = await getToken(scopes)
    if (response && response.accessToken) {
      return callGraph(uri, response.accessToken, axiosConfig)
    } else {
      throw new Error('We could not get a token because of page redirect')
    }
  } catch (error) {
    throw new Error(`We could not get a token: ${error}`)
  }
}

export const getGraphProfile = async () => {
  try {
    return await getGraphDetails(
      config.resources.msGraphProfile.uri,
      config.resources.msGraphProfile.scopes
    )
  } catch (error) {
    throw new Error(`Failed retrieving the graph profile: ${error}`)
  }
}

export const getGraphPhoto = async () => {
  try {
    const response = await getGraphDetails(
      config.resources.msGraphPhoto.uri,
      config.resources.msGraphPhoto.scopes,
      { responseType: 'arraybuffer' }
    )
    if (!(response && response.data)) {
      return ''
    }
    const imageBase64 = new Buffer(response.data, 'binary').toString('base64')
    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
    return `data:${response.headers['content-type']};base64, ${imageBase64}`
  } catch (error) {
    throw new Error(`Failed retrieving the graph photo: ${error}`)
  }
}

TypeScript 不會產生警告,只會產生錯誤。 就 TS 而言, any分配都是有效的。 這就是 linter 提供額外支持的地方。

幸運的是,您不需要復制您的界面。 使用 TypeScript 的 ReturnType 在defaultState方法中獲取profile ReturnType的類型:

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

上述行利用了 3 個出色的 TypeScript 特性:

  • ReturnType推斷 function 返回的類型
  • typeof從 object 實例推斷接口
  • ["profile"]獲取接口某個屬性的類型

現在,讓您的callGraph function 通用:

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 })
}

並在您的getGraphDetails function 中更新callGraph調用:

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

現在您的圖形調用已正確鍵入,您不必復制配置文件定義; 相反,您使用 TypeScript 出色的類型推斷技術從 function 的返回類型中“讀取您的接口”。

以相反的順序回答您的問題:

為什么 TypeScript 對此代碼沒有問題,但 linter 有問題? 兩者都不需要對齊嗎?

在 Typescript 中,類型為any的東西可以分配給任何東西 使用any本質上會從該部分代碼中刪除類型安全性。 例如:

const foo: number = 'hello' as any // Typescript is fine with this

我想該 eslint 規則的重點是捕捉您可能不想將any類型的東西實際分配給其他東西的地方。 老實說,鑒於存在編譯器選項noImplicitAny ,我不太確定為什么要使用該 linting 規則。

如何塑造 Promise getGraphProfile 中的數據使其匹配? 因為可以創建一個 TS 接口,但這只會使用 object defaultState().profile 創建重復的代碼

有幾種方法可以解決這個問題。 最簡單的方法可能是輸入getGraphDetails的返回值:

type GraphDetailsPayload = {
  id: string,
  displayName: string,
  givenName: string,
}

export const getGraphProfile = async (): Promise<GraphDetailsPayload> => {
  ...
}

但通常最好在盡可能低的級別鍵入數據,在這種情況下,這意味着callGraph function:

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

通過這樣做,現在callGraph的返回值是類型化的,因此 TS 將知道getGraphDetailsgetGraphProfile都返回相同的類型,因為它們最終只是通過 API 響應。

最后一個選項:我不使用 Axios,但我敢打賭它的 Typescript 定義會讓你這樣做:

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

我已經刪除了Promise<GraphDetailsPayload>返回類型,而是通過尖括號將GraphDetailsPayload類型“傳入”到axios function 調用。 這是利用了一種叫做“泛型”的東西,它是像 TS 這樣的類型系統中最有趣和最復雜的部分。 你會在你使用的庫中經常遇到它們,並且你最終會開始編寫使用 generics 的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM