ObjectEntry has poor behaviour for partial types
@jossmac is already working on this.
Since Feb 16, 2025.
- Dominant language
- TypeScript
- Stars
- 84
- Forks
- 1
- Avg merge
- 9m
- Merged PRs (30d)
- 2
Description
The ObjectEntry type is intended to help with consuming the pairs from Object.entries. It is currently implemented as...
export type ObjectEntry<T> = { [K in keyof T]: [K, T[K]] }[keyof T];
However its behaviour is not ideal in practice when consuming entries from a partial type (that has optional values) owing to...
- the use of
keyofwhich is systematically too broad - allowing through a partial value type, when optionality makes no sense for a entry which has been found in an object
In particular, undefined values and undefined entries are permitted through.
Expected behavior
ObjectEntry should reflect possible values from an object with optional properties (which is to say if there is an entry, it is populated, and that entries themselves cannot be undefined).
You can see an errored case of the current implementation, and a fixed implementation TypedEntryOf in the playground at https://www.typescriptlang.org/play?ts=5.3.2#code/C4TwDgpgBA8gRgKwgY2AUQHbAE4gDwAqAfFALxQDeUA2gNJQCWGUA1hCAPYBmUBAugC4atADS86fPlAC+1Npx78A3AFgAUOtCRe4CABNa7GF0IlyBKBAAewCBj0BnKAAUAhtmANXAGzxMuENhQAEokAPxQABTBljZ2jiEoHNh6fhgBQaJQAK4YLBgcAO4Y4VD0QhgQAG6BAJRQFdWBqmpa0AS6epg4IMamZJTCjMwdkAZGJsSCwmLBEACO2QzY+qYSUrKj+oa9k0R8LZq6Lt6ulcAAMhwcLNlgAxTqUFAAFhDe3hxhQgDkhcnePQ-FrPF4Mb5QH4AW3cDmB6mkhzUyA4GAcwCgnxudyEzlO5yu2Pu5EealB70+v3+2EBPxET1eDF+MOwcPpamkUFcThRaOASLaUDQVlcULA3ggWy6WFwxgGUu6spMbW4mOutzARBaAHptc8AHphdRHbTC0XiiDwJCoRUgAZWlDoGX4FU8LEarXqXUGo0aNRAA
Actual behavior
Undefined values creep in at multiple levels
Steps to reproduce
See provided playground
Suggested solution?
A preferred implementation is shown below...
type TypedKeyOf<T> = T extends Partial<infer R> ? (R extends Record<infer K, unknown> ? K : never) : never;
type TypedEntryOf<T> = { [K in TypedKeyOf<T>]: [K, Required<T>[K]] }[TypedKeyOf<T>];
What version of emery are you running?
Latest at the time of writing
Demo
type ObjectEntry<T> = { [K in keyof T]: [K, T[K]] }[keyof T];
type PlanetLookup = {
hello?: 'world';
hi?: 'mars';
};
const lookup: PlanetLookup = {
hello: 'world',
hi: 'mars',
} as const;
type ExampleObjectEntry = ObjectEntry<typeof lookup>;
// ^? // ["hello", "world" | undefined] | ["hi", "mars" | undefined] | undefined
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.