我有一个应该返回的函数NetworkState
; 但是,从代码中可以看出,在大多数情况下,该函数未返回正确的类型,但TypeScript没有给出错误。我想念什么?
第二次更新:谢谢大家的投入。我已尽可能地复制和简化了代码,完整的代码已复制并粘贴到TypeScript游乐场。不幸的是,我必须包括所有类型才能重现该问题,因此代码比我希望的要长一点。请查看我认为应该有错误(由于返回类型)而没有错误的最后一行。
非常感谢您的任何帮助,谢谢。
更新和说明:
范例1:
const someFunction = (): string => 45
上面函数的返回类型为,string
但是我们正在返回一个数字,因此TypeScript给出了一个错误:Type '45' is not assignable to type 'string'
范例2:
type MyType = {
[key: string]: string | undefined
}
const someFunction = (): MyType => 45
上面函数的返回类型为,MyType
但是我们正在返回一个数字,因此TypeScript给出了一个错误:Type '45' is not assignable to type 'MyType'
以下代码的问题:
在下面的示例中,networkStateReducer
预期返回NetworkState
类型。但是,即使它返回的数据不符合NetworkState
,也仍然没有错误。
例如,如果我们仔细观察第一个case
:
case NetworkActionType.Failure:
return { ...state, [action.payload.sagaAction]: 'SHOULD_BE_ERROR' }
假设state
最初是空对象,则返回值为:[action.payload.sagaAction]
等于某个字符串,但签名清楚地将其设置为对象:
[key in NetworkSagaAction]?: {
networkError?: Error
networkStatus: NetworkStatus
}
但是,我们没有从TypeScript得到任何错误。
实际代码:
export type NetworkSagaAction = 'Login' | 'Logout'
export type NetworkStatus = 'idle' | 'pending' | 'failure' | 'success'
export enum NetworkActionType {
Busy = 'Network/Busy',
Failure = 'Network/Failure',
Idle = 'Network/Idle',
Reset = 'Network/Reset',
Success = 'Network/Success',
}
export type NetworkState = {
[key in NetworkSagaAction]?: {
error?: Error
networkStatus: NetworkStatus
}
}
export interface NetworkPayload {
sagaAction: NetworkSagaAction
}
export const initialState: NetworkState = {}
type FunctionType = (...args: any[]) => any
interface ActionCreatorsMapObject {
[actionCreator: string]: FunctionType
}
export type ActionsUnion<A extends ActionCreatorsMapObject> = ReturnType<A[keyof A]>
export interface Action<T extends string> {
type: T
}
export interface ActionWithPayload<T extends string, P> extends Action<T> {
payload: P
}
export type BusyAction = ActionWithPayload<NetworkActionType.Busy, NetworkPayload>
export function createAction<T extends string, P>(type: T, payload: P): ActionWithPayload<T, P>
export function createAction<T extends string, P>(type: T, payload?: P) {
return payload === undefined ? { type } : { type, payload }
}
export type NetworkActions = ActionsUnion<typeof NetworkActions>
export const NetworkActions = {
busy: (payload: NetworkPayload): BusyAction => createAction(NetworkActionType.Busy, payload),
}
const networkStateReducer = (
state = initialState,
action: NetworkActions,
): NetworkState => {
return {
[action.payload.sagaAction]: 'THIS SHOULD BE OBJECT BUT NOT AND STILL NO TYPE ERROR'
}
}
TypeScript无法正确推断返回类型是什么,networkStateReducer()
因为您将返回具有计算值的对象,该对象的具体值要到运行时才能知道。实际上,您以这样的方式编写它:不可能推断返回的对象结构是什么(因此不可能抛出TS警告)。
仅考虑功能networkStateReducer()
:
const networkStateReducer = (
state = initialState,
action: NetworkActions,
): NetworkState => {
return {
[action.payload.sagaAction]: 'THIS SHOULD BE OBJECT BUT NOT AND STILL NO TYPE ERROR'
}
}
在这里,action.payload.sagaAction
将是type NetworkSagaAction
,两个字符串之一,Login
或者Logout
。TypeScript知道从此函数返回的对象可以具有与类型匹配的可选键NetworkSagaAction
,因此它可以检查这些键。但是,不知道这些键处的对象的值应该是什么。本质上,很容易根据编写的静态代码来推断键的类型,但是该键上的值的类型要等到运行时才能推断出来,因为它取决于键值。
这里的事情让您有些困惑,因为两个值的类型应该是相同的:
export type NetworkState = {
[key in NetworkSagaAction]?: {
error?: Error
networkStatus: NetworkStatus
}
}
但是,假设两个值的类型实际上不同的情况:
export type NetworkState = {
Login?: {
error?: Error
networkStatus: NetworkStatus
}
Logout?: string
}
现在,再次查看原始函数的代码:
const networkStateReducer = (
state = initialState,
action: NetworkActions,
): NetworkState => {
return {
[action.payload.sagaAction]: 'THIS SHOULD BE OBJECT BUT NOT AND STILL NO TYPE ERROR'
}
}
此函数的返回类型有效还是无效?如果值action.payload.sagaAction
是Login
那么它是无效的,因为,而不是字符串应该是一个对象。但是,如果值为,Logout
则返回类型有效,因为字符串可以。那是什么呢?谁知道!(直到运行时)
从理论上讲,现在您可能会认为,如果编写代码以确保对象的所有键都具有相同的类型,那么TypeScript应该能够推断出这一点。通过执行以下操作,我们甚至可以使其更加明确:
export type NetworkSagaActionValue = {
error?: Error
networkStatus: NetworkStatus
}
export type NetworkState = {
[key in NetworkSagaAction]?: NetworkSagaActionValue
}
但这似乎并不能解决问题。TS开发人员可能有一些非常聪明,复杂和故意的原因,为什么这不起作用。也许这只是将来会解决的疏忽或错误。
现在,我看到您有两个主要选项,它们都涉及到简单地networkStateReducer()
稍微更改功能。
您可以像下面这样从函数返回中强制实施对象值的类型:
const networkStateReducer = (
state = initialState,
action: NetworkActions,
): NetworkState => {
const returnValue: NetworkSagaActionValue = 'THIS SHOULD BE OBJECT BUT NOT AND STILL NO TYPE ERROR';
// 'returnValue' errors because types don't match
return {
[action.payload.sagaAction]: returnValue
}
}
或者,有些罗word,但根据您的需求可能会更好-只需检查action.payload.sagaAction
and的值并从那里分支,即可单独显式定义返回的对象:
const networkStateReducer = (
state = initialState,
action: NetworkActions,
): NetworkState => {
switch (action.payload.sagaAction) {
case "Login":
return {Login: 'THIS SHOULD BE OBJECT BUT NOT AND STILL NO TYPE ERROR'};
// above return errors because types don't match
case "Logout":
return {Logout: {networkStatus: 'idle'}};
}
}
我很想知道自己是否有是仅与计算的值更优雅的方式来解决这个问题的一种方式,但据我所知是没有的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句