Typescript: Can "keyof" be able to be used on Javascript objects?

Created on 20 Jan 2020  路  6Comments  路  Source: microsoft/TypeScript

Search Terms

keyof
keyof typeof

Suggestion

keyof should be a valid shorthand for keyof typeof. We describe both objects and types as having "keys" yet these are technically two different things. It is therefore unclear that keyof cannot be used on a plain javascript object, especially as typescript _is_ capable of inferring the keys of the object through the typeof operator. I think it would be helpful and less confusing if keyof was essentially equivalent to keyof typeof when used on a javascript object.

Use Cases

Reducing confusion and ambiguity around what a "key" is.

Examples

const t = {
  a: 'foo',
}

type T = keyof t;

Checklist

My suggestion meets these guidelines:

  • [*] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [*] This wouldn't change the runtime behavior of existing JavaScript code
  • [*] This could be implemented without emitting different JS based on the types of the expressions
  • [*] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [*] This feature would agree with the rest of TypeScript's Design Goals.
Working as Intended

Most helpful comment

This will cause problems if there are name collisions between values and types scopes

type obj = {a: number;};

const obj = {b: 'str'};

type T = keyof obj // a or b?

Even more problematic with classes

class Cls {
    static stat = 123;
    constructor() { }

    log() {
        console.log('logging')
    }
}

type T = keyof Cls; // log or stat?

All 6 comments

Already possible, you just need the typeof type operator to get the type of the value:

const t = {
  a: 'foo',
}

type T = keyof typeof t;

Playground Link

This will cause problems if there are name collisions between values and types scopes

type obj = {a: number;};

const obj = {b: 'str'};

type T = keyof obj // a or b?

Even more problematic with classes

class Cls {
    static stat = 123;
    constructor() { }

    log() {
        console.log('logging')
    }
}

type T = keyof Cls; // log or stat?

We generally don't ever make this kind of guesswork because it ends up creating more confusion than it's worth.

@dragomirtitian my issue specifically states this would be shorthand for that syntax

@IllusionMH that's no more ambiguous than:

class Cls {
    static stat = 123;
    constructor() { }

    log() {
        console.log('logging')
    }
}

type T = keyof typeof Cls; // log or stat?

which is the equivalent of what I'm proposing

@RyanCavanaugh Maybe I didn't communicate this property, it wouldn't be guesswork, it would just be shorthand, to try and make more of an equivalence between "keys" in javascript and "keys" in typescript.


Unfortunately it seems that the name collision scuppers this specific proposal but I still think there is room for improvement here.

TL;DR: It's impossible to make keyof shorthand without breaking change and avoid collisions(=guesswork).

that's no more ambiguous than ... keyof typeof Cls

Confusing at first and requires knowledge of TS behavior (know that there is 2 scopes: types and values, that keyof works on types scopes and typesof works on values scope, class declarations creates records in both and it's instance type which will be in type scope) - YES!

Ambiguous in terms where keys come from (types or values scope) - NO!
keyof typeof Cls explicitly states that keys should be taken from constructor function type (typeof Cls) and not an instance.

It's impossible to make keyof Cls to use Cls from values scope because it will break all existing code. So it's impossible to use shorthand for classes without breaking change and needs special handling in code and new rule of thumbs for users.

For case with object literal - it would look great at first, however keys will be wrong as soon as there will be name collision between scopes.
So either keyof should always work with values scope or it will be guesswork from users and problems if there are name collision.

IMO it is clear to me that the keyof a class would be the static members, classes themselves are not instantiated objects. But i agree it would be a breaking change because of collisions of scope, so that's fine.

Was this page helpful?
0 / 5 - 0 ratings