Skip to main content

Type Alias: FlatEntryInput<TProps>

FlatEntryInput<TProps> = string extends keyof TProps ? 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>