Description
The signature of Object.getOwnPropertyNames is getOwnPropertyNames(o: any): string[];
Would it be possible to get getOwnPropertyNames<T>(o: T): (keyof T)[];
instead?
The same for Object.keys: keys<T>(o: T): (keyof T)[];
If I'm correct, this doesn't cause any edge cases, but allows us to write more typesafe code. Right now, I'm forced to cast some things.
Here's a toy example:
const Suits = {
clubs: { toString: () => '\u2663' },
diamonds: { toString: () => '\u2666' },
hearts: { toString: () => '\u2665' },
spades: { toString: () => '\u2660' }
}
type Suit = keyof typeof Suits;
const Ranks = {
ace: { valueOf: () => 1, toString: () => 'A' },
deuce: { valueOf: () => 2, toString: () => '2' },
three: { valueOf: () => 3, toString: () => '3' },
four: { valueOf: () => 4, toString: () => '4' },
five: { valueOf: () => 5, toString: () => '5' },
six: { valueOf: () => 6, toString: () => '6' },
seven: { valueOf: () => 7, toString: () => '7' },
eight: { valueOf: () => 8, toString: () => '8' },
nine: { valueOf: () => 9, toString: () => '9' },
ten: { valueOf: () => 10, toString: () => 'T' },
jack: { valueOf: () => 11, toString: () => 'J' },
queen: { valueOf: () => 12, toString: () => 'Q' },
king: { valueOf: () => 13, toString: () => 'K' }
}
type Rank = keyof typeof Ranks;
type Card<R extends Rank, S extends Suit> = { rank: R, suit: S }
let deck = Object
.keys(Suits)
.reduce<Card<Rank, Suit>[]>((cards, suit) =>
cards.concat(Object
.keys(Ranks)
.map(rank => ({
rank: <Rank>rank, // Should be inferred instead
suit: <Suit>suit // Should be inferred instead
}))
),
[]);
As you can see, at the end there I'm forced to cast rank and suit, they should be inferred. If I change lib.d.ts to have the signature I suggest, it correctly infers the types there. It shouldn't cause edge cases or break code as I can't imagine there's any code that expects Object.keys to return a string that's not a key of that object.
The above code is MIT license btw, if anyone feels like using it.