代码之家  ›  专栏  ›  技术社区  ›  DarkLite1

@typescript eslint/no unsafe赋值:任意值的不安全赋值

  •  0
  • DarkLite1  · 技术社区  · 4 年前

    考虑以下代码:

    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赋值:任何值的不安全赋值。

    这意味着 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 但这只会创建与对象重复的代码 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}`)
      }
    }
    
    1 回复  |  直到 4 年前
        1
  •  8
  •   Arash Motamedi    4 年前

    TypeScript不会产生警告,只会产生错误。就TS而言 any 作业有效。这就是过梁提供额外支撑的地方。

    幸运的是,你不需要复制你的界面。使用TypeScript ReturnType 要获取的类型 profile 对象在你 defaultState 方法:

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

    上面的代码使用了3个伟大的TypeScript特性:

    • 退货类型 推断函数返回的类型
    • typeof 从对象实例推断接口
    • ["profile"] 获取接口的某个属性的类型

    现在,让你 callGraph 通用功能:

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

    并更新 callGraph 打电话给你 getGraphDetails 功能:

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

    现在,您的图形调用已正确键入,您不必复制配置文件定义;相反,您使用TypeScript的出色类型推理技术从函数的返回类型“读取接口”。

        2
  •  5
  •   Kas Elvirov    4 年前

    按照相反的顺序回答您的问题:

    为什么TypeScript对这段代码没有问题,但linter却有问题?两者都不需要结盟吗?

    在Typescript中,有类型的东西 any 可以分配给 任何东西 .使用 任何 基本上从代码的这一部分中删除了类型安全。例如:

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

    我想eslint规则的重点是捕捉那些你可能不想实际分配类型的地方 任何 到别的东西。说实话,考虑到编译器选项,我不太确定为什么要使用linting规则 noImplicitAny 存在。

    如何对Promise getGraphProfile中的数据进行整形,使其匹配?因为可以创建TS接口,但这只会创建具有对象defaultState().profile的重复代码

    有几种方法可以解决这个问题。最简单的方法可能是键入返回值 getGraphDetails :

    type GraphDetailsPayload = {
      id: string,
      displayName: string,
      givenName: string,
    }
    
    export const getGraphProfile = async (): Promise<GraphDetailsPayload> => {
      ...
    }
    

    但通常最好以尽可能低的级别键入数据,在这种情况下,这意味着 callGraph 功能:

    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将知道 getGraph详细信息 getGraphProfile 两者都返回相同的类型,因为它们最终只是通过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 函数调用。这是在利用一种名为“泛型”的东西,泛型是TS等类型系统中最有趣、最复杂的部分。你会在你使用的库中遇到很多泛型,最终你也会开始编写接受泛型的函数。