Repository: TypeScript
Issue: Predicate `value is object` doesn't work in Array.prototype.filter()
Labels: Suggestion,Awaiting More Feedback

# Bug Report

<!--
  Please fill in each section completely. Thank you!
-->

### 🔎 Search Terms

<!--
  What search terms did you use when trying to find an existing bug report?
  List them here so people in the future can find this one more easily.
-->

type narrowing, type predicate, `value is object`, `filter()`, `lodash.isObject`

### 🕗 Version & Regression Information

<!-- When did you start seeing this bug occur?

"Bugs" that have existed in TS for a long time are very likely to be FAQs; refer to
  https://github.com/Microsoft/TypeScript/wiki/FAQ#common-bugs-that-arent-bugs

If possible, please try testing the nightly version of TS to see if it's already been fixed.
For npm: `typescript@next`
This is also the 'Nightly' version in the playground: http://www.typescriptlang.org/play/?ts=Nightly

Note: The TypeScript Playground can be used to try older versions of TypeScript.

Please keep and fill in the line that best applies:
-->
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about [type guards](https://github.com/Microsoft/TypeScript/wiki/FAQ#type-guards).

### ⏯ Playground Link

<!--
  A link to a TypeScript Playground "Share" link which shows this behavior

  The TypeScript Workbench can be used for more complex setups, try
  https://www.typescriptlang.org/dev/bug-workbench/

  As a last resort, you can link to a repo, but these will be slower for us to investigate.
-->
[Playground link with relevant code](https://www.typescriptlang.org/play?ts=3.3.3#code/PTAEEsGcDkFcFsBGBTATqAhgOwCYUgPKIBWyAxgC6arKjwD2OyANsnhgGYVqgUAWtDrCyVw9LJFCIAnrwGhIGeLSxLkk8FlDNGGSHwB0AWABQpoSIpitUOEjQAKAG4ZmsZAH4AXJizSAlD4ubrRQoFgIKOgA3qagoDQUsKhaFNIADsj0HKDB7qAAvEWgAEQR9qglANymAL6m5sKi4vhEpJTOru7evgFBXaGS9CTkVLEm8YnJqRlZOXm0RQWlw+0U1XUNJiCgAKIYZHygLMjKWFRk9LDMeCi+oKujD+gY4ZFoADQK9KAA7rRkbAAcioFBoGCo4CoemOUIEqGMJkuEioLlQYlgkAAKnxNABzSTLADa0UwPgAjKBal9yV8ShhEGQSgBdGomNEY7G4rAEokABmZoAA1KByWyOVcufjIPzmQYMGzTDsAGLgZjcdE80AcejocTMWTlKKSaTgFg4SQLST-UCArQYHA4L6IWBULD0Kh45BUdKoeiZVBWdQPDiI5GQaHMZh2Y2FXIYdGSnHSgwcNUahy2d6ofxs1zR7MygXC0V5qMxtBFuUKrY7ABCru16bQ+O1uoeWANDxGlFApvNkg4GDVkgoPxoDCcKmz2r98DktGE1l4szD4gjmCjbVGhPjicxyZ5kFTzdQmcIPYoudM+e3lCrJbFN63l6r8sV2zAAE1va1L-hMEQVheB+VRUD9X5MAUfFgJOM5IXtaD4HSYDkRwKFrFcNcUXjEI4wlA9uV5AU2XAHJzzvChOhCfx-FAcZ4niBZ5U2EwgA)

### 💻 Code

<!-- Please post the relevant code sample here as well-->
```ts
// isNumber and isObject are modeled after the functions by the same names in lodash.

function isNumber(value?: any): value is number {
  return typeof value === "number";
}

function isObject(value?: any): value is object {
  return typeof value === "object";
}

// Each element could be an object or a number, so we can't treat it as either.
const variousThings = [{ a: 1 }, 1, "abc"];
variousThings[0] + 1;
variousThings[0].a;

// Filtering for only numbers yields values we can add, but not get properties of.
const allNumbers = variousThings.filter(isNumber);
allNumbers[0] + 1;
allNumbers[0].a;

// But filtering for only object yields fails to remove number from the union type.
const allObjects = variousThings.filter(isObject);
allObjects[0] + 1;
allObjects[0].a;

// Yet isObject is able to narrow a single element in a simple conditional.
const value = variousThings[0];
if (isObject(value)) {
    value.a
}
```

### 🙁 Actual behavior

`allObjects` was typed as `(string | number | { a: number })[]`, so both lines below `const allObjects` fail.

### 🙂 Expected behavior

`allObjects` should be typed as `{ a: number }[]`, so `allObjects[0].a` should pass the type checker. Notably, as seen above, `isObject` *does* manage to narrow the type of a single value with the union type to the only member of the union that's an object, it just can't narrow all of the elements using `.filter()` the way `isNumber` is able to.

I can't conceive of why they behave differently, but I also don't know the internals well. I *think* this difference is undesired, but I might be missing something deeper.