Причина для типа CombinedState в типах машинописного текста - PullRequest
1 голос
/ 22 сентября 2019

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

В комментариях говорится, что раньше он отличал его от {} или пустого типа, но я нигде не вижу, чтобы он проверял этот тип, и даже в комментариях говорится, что внутренне мы никогда не проверяем его значение, так почемуэто даже там кроме того, чтобы сбить с толку таких людей, как я?

1 Ответ

2 голосов
/ 22 сентября 2019

Поскольку в комментарии упоминается, что машинопись написана по типу утки, это означает, что вы можете присваивать, казалось бы, несвязанные типы друг другу:

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, вызывает другое поведение в условном типе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...