microsoft/TypeScript

Object.keys should return `keyof T`

fregante opened this issue Β· 6 comments

TypeScript Version: 3.6.0-dev.20190717

Code

const feature = {
	description: '',
	screenshot: '',
	disabled: ''
};

const list = Object.keys(feature);

Expected behavior:

keys<T extends object>(o: T): Array<keyof T>;
typeof list = Array<"description" | "screenshot" | "disabled">

Actual behavior:

keys(o: {}): string[];
typeof list = string[]

Workaround:

const feature = {
	description: '',
	screenshot: '',
	disabled: ''
};

const list = Object.keys(feature) as Array<keyof typeof feature>;

Playground Link: https://www.typescriptlang.org/play/#src=const%20feature%20%3D%20%7B%0D%0A%09description%3A%20''%2C%0D%0A%09screenshot%3A%20''%2C%0D%0A%09disabled%3A%20''%0D%0A%7D%3B%0D%0A%0D%0Aconst%20keys%20%3D%20Object.keys(feature)%3B

Object.keys() does not return (keyof T)[] in the general case.

In some specific instances, it will. In most others, it will not.

declare const x : { a : number };
Object.keys(x);

You might expect the above to return ["a"] but it's possible for it to return ["a", "b"], too.

Because {a: number, b: number} is assignable to {a: number}


If anything, we can only say Object.keys() returns keyof T if,

  • We have no number keys
  • We have no symbol keys
  • keyof T does not contain keys from types in the parent and ancestor [[prototype]]
  • T does not have optional properties
  • T is some kind of "strict" object that does not allow extra properties (TS doesn't have such a concept at the moment)

I'm sure there are other constraints I haven't thought of.

[Edit]
Oh. The linked Stack Overflow answer explains it better

Duplicate and rejected multiple times: #12253 (comment), #30314, #13254. #30228, #28899, #28284, #26901, #30749, #31087.

more info

Maybe we should make a template just for this. It'd save people a lot of time.

@RyanCavanaugh I actually just copied my last comment and added the other issue. But this is a good candidate for the FAQ (but I doubt it's being checked before).

It’s because it sounds so obvious that I just checked the open issues before opening this one πŸ˜… Nobody expects keyof to be different from keys()