Redux具有以下类型:
declare const $CombinedState: unique symbol
/**
* State base type for reducers created with `combineReducers()`.
*
* This type allows the `createStore()` method to infer which levels of the
* preloaded state can be partial.
*
* Because Typescript is really duck-typed, a type needs to have some
* identifying property to differentiate it from other types with matching
* prototypes for type checking purposes. That's why this type has the
* `$CombinedState` symbol property. Without the property, this type would
* match any object. The symbol doesn't really exist because it's an internal
* (i.e. not exported), and internally we never check its value. Since it's a
* symbol property, it's not expected to be unumerable, and the value is
* typed as always undefined, so its never expected to have a meaningful
* value anyway. It just makes this type distinquishable from plain `{}`.
*/
export type CombinedState<S> = { readonly [$CombinedState]?: undefined } & S
对于这个符号和类型的用途,我有点头绪。
combineReducers
export default function combineReducers<S>(
reducers: ReducersMapObject<S, any>
): Reducer<CombinedState<S>>
export default function combineReducers<S, A extends Action = AnyAction>(
reducers: ReducersMapObject<S, A>
): Reducer<CombinedState<S>, A>
export default function combineReducers<M extends ReducersMapObject<any, any>>(
reducers: M
): Reducer<
CombinedState<StateFromReducersMapObject<M>>,
ActionFromReducersMapObject<M>
>
export default function combineReducers(reducers: ReducersMapObject) {
const reducerKeys = Object.keys(reducers)
const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
评论说它曾经与{}
或空类型区分开来,但是我看不到任何地方检查它,甚至在评论中说我们内部从不检查它的值,为什么呢?甚至在那里除了让我这样的人迷惑之外?
答案 0 :(得分:2)
正如评论中提到的打字稿是鸭式打字,这意味着您可以将看似无关的类型分配给另一个:
type A = { foo: string, baz?: string }
type B = { foo: string, goo?: number }
declare let a: A;
declare let b: B;
// Unrelated types, structurally compatible, so assignable
a = b;
b = a;
此行为称为structural typing,与其他强类型语言(例如Java或C#)的工作方式不同。更多传统语言使用nominal typing,其结构无关紧要,并且除非有某些继承关系,否则A
不能分配给B
。
要模拟类似于打字稿中标称键入的内容,我们可以使用标称行为的专用字段或unique symbol
。这意味着结构无关紧要,在确定兼容性时,只有两个unique symbol
来自相同的定义才是兼容的。
这意味着对于CombinedState
,类型系统将能够保证,如果类型为T extends { [$CombinedState]: undefined }
,则它必须来自combineReducers
(或者至少是它的实例化) CombinedState
)。这是有保证的,因为客户端无法访问$CombinedState
,因此只能用于库中。
为什么这很重要?很可能是由于PreloadedState
:
/**
* Recursively makes combined state objects partial. Only combined state _root
* objects_ (i.e. the generated higher level object with keys mapping to
* individual reducers) are partial.
*/
export type PreloadedState<S> = Required<S> extends {
[$CombinedState]: undefined
}
? S extends CombinedState<infer S1>
? {
[K in keyof S1]?: S1[K] extends object ? PreloadedState<S1[K]> : S1[K]
}
: never
: {
[K in keyof S]: S[K] extends object ? PreloadedState<S[K]> : S[K]
}
在PreloadedState
中,S
是CombinedState
的事实在条件类型中触发了不同的行为。