Поскольку в комментарии упоминается, что машинопись написана по типу утки, это означает, что вы можете присваивать, казалось бы, несвязанные типы друг другу:
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;
Play
Это поведениеназывается структурной типизацией и отличается от того, как работают другие строго типизированные языки (такие как Java или C #).В более традиционных языках используется номинальная типизация , где структура не имеет значения, и, за исключением некоторых отношений наследования, A
не может быть назначен на B
.
Для имитации чего-либо, похожего наДля номинальной типизации в машинописи мы можем использовать либо приватные поля, либо unique symbol
s, которые ведут себя номинально.Это означает, что структура не имеет значения, при принятии решения о совместимости два 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
, вызывает другое поведение в условном типе.