![](/img/trans.png)
[英]ESLint: Unsafe assignment of an `any` and Unsafe call of an `any` typed value
[英]@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 }
getGraphProfile
中的数据使其匹配? 因为可以创建一个 TS interface
,但这只会使用 object defaultState().profile
创建重复的代码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 将知道getGraphDetails
和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
function 调用。 这是利用了一种叫做“泛型”的东西,它是像 TS 这样的类型系统中最有趣和最复杂的部分。 你会在你使用的库中经常遇到它们,并且你最终会开始编写使用 generics 的函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.