Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Situations where object literal type inference fails #36549

Closed
harrysolovay opened this issue Jan 31, 2020 · 2 comments
Closed

Situations where object literal type inference fails #36549

harrysolovay opened this issue Jan 31, 2020 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@harrysolovay
Copy link

Search Terms: "infer", "object", "literal", "key", "value", "entry", "entries", "type", "signature", "keyof"

While digging into the keyofStringsOnly flag, I realized the following.

Code

Let's say we have this object literal:

const obj = {
  a: 1,
  b: true,
  c: "yo",
};

It has a finite number of possible keys/values/entries. In many cases, this is true for non-literals as well:

interface Obj { a: 1; b: true; c: "yo"; }
declare function getObj(): Obj;
// we can be certain that `obj` will have the `Obj` signature
const obj = getObj();

Let's try to get the key, value and entry types from obj.

// expected signature of `[string, 1 | true | "yo"][]`
// actual signature of `[string, string | number | boolean][]`
const entries = Object.entries(obj);

// expected signature of `"a" | "b" | "c"`
// actual signature of `"a" | "b" | "c"`
type KeyOfType = keyof typeof obj;

// expected signature of `{ a: 1; b: true; c: "yo"; }`
// actual signature of `{ a: number; b: boolean; c: string; }`
type ObjType = typeof obj;

// expected signature of `1 | true | "yo"`
// actual signature of `string | number | boolean`
type ValueType = ObjType[KeyOfType];

I've played around with my TSConfig to see if I can get the correct behavior. keyofStringsOnly nor strictMode make a difference.

In discussions such as #35745, some users state that it's a 3rd-party library's responsibility to correct this (IMO seemingly-faulty) inference. A custom entries function––for example––could make use of the Exclude utility type. While getting the correct signature is certainly possible in the language, I believe it should––in the cases above––be the default.

Please let me know if I'm missing something, or haven't the right configuration. Any help would be greatly appreciated. Thank you!

Playground Link:
here

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jan 31, 2020
@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Jan 31, 2020

This is the correct behavior because object types aren't sealed. You could have written this, for example:

interface Obj { a: 1; b: true; c: "yo" }
function getObj(): Obj {
    const result = { a: 1, b: true, c: "yo", d: {} } as const;
    return result;
}

in which case your entries result will have a ["d", {}] entry - {} is not string | number | boolean.

@harrysolovay
Copy link
Author

@RyanCavanaugh how could I have ever doubted the thinking behind this was intentional! Thank you for explaining.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants