Skip to content

Suggestion: treat in operator as type guard which asserts property existence #21732

Closed

Description

TypeScript Version: 2.8.0-dev.20180204

Search Terms: in operator type guard generic assert

Code

function f<K extends string, T extends object>(key: K, genericObj: T, concreteObj: {foo: string}) {
  if ('a' in concreteObj) {
    concreteObj.a // error, Property 'a' does not exist on type 'never'.
  }
  if ('a' in genericObj) {
    genericObj.a // error, Property 'a' does not exist on type 'T'.
  }
  if (key in concreteObj) {
    concreteObj[key]; // error, Type 'K' cannot be used to index type '{ foo: string; }'
  }
  if (key in genericObj) {
    genericObj[key] // error, Type 'K' cannot be used to index type 'T'.
  }
}

Actual behavior:
The compiler does not recognize that the objects have relevant keys even after checking for the existence of the key with the in operator. According to a comment by @sandersn, the in type guard (as implemented in #15256) narrows by eliminating members from a union; it does not assert that a key exists.

Desired behavior:
The compiler would assert that each object had a property with the relevant key, after having checked for the existence of the key with the in operator. Note that one possible implementation of an asserting type guard would look like

function inOperator<K extends string, T extends object>(k: K, o: T): o is T & Record<K, unknown> {
  return k in o;
}

but this does not behave exactly as desired, possibly due to the bug in #18538: (not sure if #18538 was officially fixed, but it's not erroring anymore)

function g<K extends string, T extends object>(key: K, genericObj: T, concreteObj: { foo: string }) {
  if (inOperator('a', concreteObj)) {
    concreteObj.a // okay
  }
  if (inOperator('a', genericObj)) {
    genericObj.a // okay
  }
  if (inOperator(key, concreteObj)) {
    concreteObj[key]; // okay
  }
  if (inOperator(key, genericObj)) {
    genericObj[key] // okay
  }
}

If a fix for #18538 appears and makes the g() function compile without error, great. Otherwise, maybe the property assertion for in could happen some other way. Not sure.

Playground Link: Here

Related Issues:
#10485, Treat in operator as type guard
#15256, Add type guard for in keyword
#18538, Error when mixing keyof and intersection type and type variable (fixed?)

(EDIT: #18538 seems to be fixed)
(EDIT: change any to unknown)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    CommittedThe team has roadmapped this issueEffort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Fix AvailableA PR has been opened for this issueHelp WantedYou can do thisSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions