Keysof type operator by weswigham · Pull Request #10425 · microsoft/TypeScript (original) (raw)

@aluanhaddad I can understand why you expect keysof and Object.keys to refer to the same concept (they both have the word "keys" in them, after all).

That said, I'd find it unfortunate if this very useful operator was restricted to enumerable-own properties.

Here's an example scenario I ran into today that got me thinking about this feature again: creating a hard-fail version of the index-for-friendly-access trick:

class X { constructor(private name: string) {} }

let x = new X("Godfrey"); x['name'] // type checks, "Godfrey", type is string, as expected x['nmae'] // type checks, undefined, type is any, unexpected

In other words, this trick conflates the elimination of privacy checks with a willingness to accept successful type-checking and any as a result if the property doesn't exist at all.

We could use the keysof operator to create a safer version of the same operator:

class X { constructor(private name: string) {} }

let x = new X("Godfrey"); x['name'] // type checks, value is "Godfrey", type is string, as expected x['nmae'] // type checks, value is undefined, type is any, unexpected

function get<T, K extends keysof T>(obj: T, key: K) { return obj[key]; }

let x = new X("Godfrey"); get(x, 'name') // type checks, "Godfrey", type is string (?), as expected get(x, 'nmae') // fails to type check

Whether this works or not depends on whether keysof includes private or protected fields, and whether the type inferencer expands get correctly.

Regardless of those details, this illustrates that there may be valid use-cases for keysof working both against private/protected fields, against, non-enumerable fields, and against non-own fields.

My own perspective is that keysof should match the semantics of the no-implicit-any indexing operator: in other words, a key is in keysof (typeof val) if typeof val[key] is a type known to the type system. Pairing a keysof type check with the indexing operator seems eminently reasonable to me.

This might also argue for a few flavors of keysof, but matching semantics already encoded in the type system in some way seems like a pre-requisite for quick inclusion.

Finally, I also wonder whether not encoding enumerable/own in the type system will be practical in the long-term, especially considering the soon-to-be-included object-spread ({ ...a, ...b }) operator in JavaScript.