Type Alias: FlatEntryInput<TProps>
FlatEntryInput<
TProps> =stringextends keyofTProps?EntryInput<TProps> :object&{ [K in keyof EntryEnvelope<TProps>]?: EntryEnvelope<TProps>[K] }&object& { [K in Exclude<keyof TProps, keyof EntryEnvelope<TProps> | "id" | "props">]?: TProps[K] }
Defined in: model/stored-entry.ts:156
What entries.add() and the constructor's entries array both take (ADR 0011, #281): a
declared Field key sits flat, at the top, the same shape update() takes —
entries.add({ id, name, owner: 'Ali' }) — and nested props still works for a bag already held
or a passenger key ingest does not know about.
This type checks every TProps key flat, whether or not a Field declares it — ingest does not
(propsFromInput, data/entry-reader.ts). A TProps key with no matching Field is an
undeclared key at ingest (ADR 0011: "an undeclared key is never written by the library, ever"):
entries.add({ id, passengerKey: 1 }) type-checks and then warns and drops passengerKey at
runtime. This is not a gap FlatEntryInput could close — declaring a key is what tells the
library the home is safe to write to (ADR 0011, "declaring a key does not create it. Declaring
says what the library may do with it"), and TProps alone does not declare one. Nest the value
under props instead (entries.add({ id, props: { passengerKey: 1 } })), which ingest always
carries, declared or not.
Conditional on whether TProps is declared. An open TProps (the default, or a plugin author's
own erased Record<string, unknown>) has no declared key to check flatly, so this falls back to
plain EntryInput<TProps> — exactly today's shape, already sound for that case. keyof an open
record is string, an index signature; had the declared-key arm below run for that branch too,
its own mapped [K in keyof TProps] would grow that same index signature and refuse the named
EntryInput<TProps> value all over again — the identical trap & Partial<TProps> fell into, just
moved. A concrete TProps (a consumer's own declared props interface) has no index signature, so
the declared-key arm runs and the flat key type-checks.
The declared-key arm is inlined here, not a named helper type: a helper api-extractor cannot see
through a non-exported name (ae-forgotten-export), and this arm is never named on its own — a
consumer names FlatEntryInput, never a "declared" half of it. Every piece — the envelope,
props, and the declared keys — is its own mapped type, re-derived from EntryEnvelope/TProps
the way EntryEdit already builds itself below, never an intersection with the named EntryInput
interface. That is the whole fix: a mapped type carries no interface identity for TypeScript to
refuse, so where EntryInput<TProps> & Partial<TProps> (the shape an earlier draft first suggested)
was uninhabitable by a named EntryInput<TProps>[] value, this type takes one straight in — it is
a subset of what this type allows, entry.toInput()'s return included.
Type Parameters
TProps
TProps = Record<string, unknown>