Object.entries
Typings of Object.entries() returned [key, value] tupple array is very weak and infer poorly from argument, I think it would be better to implement more relevant ones in lib.es2017.object.d.ts
When iterating on an object entries array returned by Object.entries, we need to manually type cast the array to avoid false positive and false negative typings shortcomings.
const a = Object.entries({foo: "bar", baz: 0}); // [string, string | number][]
const b = Object.entries({foo: "bar", baz: 0} as const); // [string, 0 | "bar"][]
It is not possible to infer typings in .mapor .forEach
a.forEach(entry => {
if(entry[0] === "foo") {
let value = entry[1]; // string | number
entry[1].toUpperCase(); // error (false positive)
} else if (entry[0] === "baz") {
let value = entry[1]; // string | number
} else if (entry[0] === "invalid") { // no error (false negative)
// ...
}
})
b.forEach(entry => {
if(entry[0] === "foo") {
let value = entry[1]; // 0 | "bar"
}
})
We can easily write our own entries function:
type Entries<T> = {
[K in keyof T]: [K, T[K]]
}[keyof T][]
function entries<T>(obj: T): Entries<T> {
return Object.entries(obj) as any;
}
Wich gives me an array of tupples with inferables key/value:
const c = entries({foo: "bar", baz: 0}); // (["foo": string] | ["baz": number])[]
const d = entries({foo: "bar", baz: 0} as const); // (["foo": "bar"] | ["baz": 0])[]
c.forEach(entry => {
if (entry[0] === "foo") {
let value = entry[1]; // string
entry[1].toUpperCase(); // no error
} else if (entry[0] === "baz") {
let value = entry[1]; // number
}
if (entry[0] === "invalid") { // error: "baz" and "invalid" have no overlap
// ...
}
});
d.forEach(entry => {
if (entry[0] === "foo") {
let value = entry[1]; // "bar"
} else if (entry[0] === "baz") {
let value = entry[1]; // 0
}
if(entry[1] === "bar") {
let key = entry[0]; // "foo"
}
});
We may implement a better default behaviour for Object.entries in the lib:
/**
* Returns an array of key/values of the enumerable properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
entries<T>( o: T): T extends ArrayLike<infer U> ? [string, U][] : { [K in keyof T]: [K, T[K]] }[keyof T][];
The same concepts can be applied for better typings of Object.values
/**
* Returns an array of values of the enumerable properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
values<T>(o: T): T extends ArrayLike<infer U> ? U[] : (T[keyof T])[];
My suggestion meets these guidelines:
- [ ] This wouldn't be a breaking change in existing TypeScript/JavaScript code
I need some feedback to identify potential breaking changes.
I think it 'may' induce breaking change since it's reducing typecast necessity. If manual casts are incompatible with the current feature request, there may be a need to remove cast or force through an intermediary any.
I tested it in a project of mine and had no issue since the existing cast was correctly made with the same source of truth as the Object.values argument type.
- [ ] This feature would agree with the rest of TypeScript's Design Goals.
I can't say I'm sure it does. Feedback would be appreciated.
Duplicate of #20322. The issue boils down to: https://github.com/microsoft/TypeScript/pull/12253#issuecomment-263132208
Hello @MartinJohns @fatcerberus and @RyanCavanaugh
I red previous issues, the 2016 Anders comment and the stackoverflow answer.
I am not convinced though, I thinks some assumptions can be challenged in regards of the current state of typescript.
In particular, contravariance issues are widespread and don't always lead to any fallback.
Would you mind refreshing this topic and allowing me to advocate another opinion ?
If you think there's some solution available, please do describe
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.