Change:
In we landed a change to the Flow types in , making the types for the various types recursive with their and properties. On that PR we may have been running an older version of Flow - I'm not having luck finding the record of when Flow ran for that PR, so not sure. In any case, that PR was merged even though it had the recursive Flow type definitions, plus one or more Flow errors which were unrelated and not detected. The problem with the recursive type definitions is that recent versions of Flow will go into an infinite loop if they try to infer types which are recursive. The solution may be to add more detail to those types, but to unblock things we temporarily made them looser type definitions.

Discussion:
const Resolved = 2; const Rejected = 3; <del> type EmptyRecord<K, V> = {| </del> <ins> type EmptyRecord<K> = {| </ins> status: 0, suspender: null, key: K, value: null, error: null, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; type PendingRecord<K, V> = {| status: 1, <del> suspender: Promise<K, V>, </del> <ins> suspender: Promise<V>, </ins> key: K, value: null, error: null, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; type ResolvedRecord<K, V> = {|

key: K, value: V, error: null, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; <del> type RejectedRecord<K, V> = {| </del> <ins> type RejectedRecord<K> = {| </ins> status: 3, suspender: null, key: K, value: null, error: Error, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; type Record<K, V> = <del> | EmptyRecord<K, V> </del> <ins> | EmptyRecord<K> </ins> | PendingRecord<K, V> | ResolvedRecord<K, V> <del> | RejectedRecord<K, V>; </del> <ins> | RejectedRecord<K>; </ins> type RecordCache<K, V> = {| map: Map<K, Record<K, V>>, head: Record<K, V> | null, tail: Record<K, V> | null, <ins> size: number, </ins> |}; // TODO: How do you express this type with Flow?

const MAX_SIZE = 500; const PAGE_SIZE = 50; <del> function createRecord<K, V>(key: K): EmptyRecord<K, V> { </del> <ins> function createRecord<K>(key: K): EmptyRecord<K> { </ins> return { status: Empty, suspender: null,

export function createCache(invalidator: () => mixed): Cache { const resourceMap: ResourceMap = new Map(); <del> function accessRecord<K, V>(resourceType: any, key: K): Record<V> { </del> <ins> function accessRecord<K, V>(resourceType: any, key: K): Record<K, V> { </ins> if (__DEV__) { warning( typeof resourceType !== 'string' && typeof resourceType !== 'number',

return newHead; } <del> function load<V>(emptyRecord: EmptyRecord, suspender: Promise<V>) { const pendingRecord: PendingRecord<V> = (emptyRecord: any); </del> <ins> function load<K, V>(emptyRecord: EmptyRecord<K>, suspender: Promise<V>) { const pendingRecord: PendingRecord<K, V> = (emptyRecord: any); </ins> pendingRecord.status = Pending; pendingRecord.suspender = suspender; suspender.then( value => { // Resource loaded successfully. <del> const resolvedRecord: ResolvedRecord<V> = (pendingRecord: any); </del> <ins> const resolvedRecord: ResolvedRecord<K, V> = (pendingRecord: any); </ins> resolvedRecord.status = Resolved; resolvedRecord.suspender = null; resolvedRecord.value = value;

error => { // Resource failed to load. Stash the error for later so we can throw it // the next time it's requested. <del> const rejectedRecord: RejectedRecord = (pendingRecord: any); </del> <ins> const rejectedRecord: RejectedRecord<K> = (pendingRecord: any); </ins> rejectedRecord.status = Rejected; rejectedRecord.suspender = null; rejectedRecord.error = error;

miss: A => Promise<V>, missArg: A, ): void { <del> const record: Record<V> = accessRecord(resourceType, key); </del> <ins> const record: Record<K, V> = accessRecord(resourceType, key); </ins> switch (record.status) { case Empty: // Warm the cache.

miss: A => Promise<V>, missArg: A, ): V { <del> const record: Record<V> = accessRecord(resourceType, key); </del> <ins> const record: Record<K, V> = accessRecord(resourceType, key); </ins> switch (record.status) { case Empty: // Load the requested resource.