Your code is currently facing multiple issues that are preventing it from functioning correctly.
Firstly, by annotating the type of data
as Entry[]
, you are instructing the compiler to discard any specific information about the initializing array literal. This means that only the information that name
is a string
is retained within the Entry
. However, if you want to maintain the string literal types of the name
properties, consider using a const
assertion instead of annotating.
To ensure that the initializer matches Entry[]
, you can utilize the satisfies
operator like this:
const data = [
{ tld: 'de', name: 'Germany', population: 83623528 },
{ tld: 'at', name: 'Austria', population: 8975552 },
{ tld: 'ch', name: 'Switzerland', population: 8616571 }
] as const satisfies Entry[];
Subsequently, although TypeScript now understands "Germany"
, "Austria"
, and "Switzerland"
, it lacks knowledge regarding the keys produced by the Object.fromEntries()
method. The current type definition specifies an output with a string
index signature.
If you wish to define a more specific call signature yourself, you can do so in your code base and merge it in accordingly:
// declare global {
interface ObjectConstructor {
fromEntries<E extends readonly [PropertyKey, any][]>(
entries: E
): { [T in E[number] as T[0]]: T[1] };
}
// }
This implementation iterates over the elements of entries
and uses key remapping to generate an object type.
Once these adjustments have been made, your dictionary
will still contain a union of const
-asserted values as its property value type. To correct this and ensure only Entry
is present, use item satifies Entry as Entry
.
Finally, here is the refined version:
let dictionary =
Object.fromEntries(data.map(item => [item.name, item satisfies Entry as Entry]));
/* let dictionary: {
Germany: Entry;
Austria: Entry;
Switzerland: Entry;
} */
You have now achieved the intended behavior for your code. Whether the modifications are worth it depends on your specific use cases.
Playground link to code