Skip to content

Discriminate return type based on property in input object #31335

Closed
@timsuchanek

Description

@timsuchanek

TypeScript Version: 3.5.0-dev.20190509

Search Terms:
discriminate return type, keyof, extends

Expected behavior:
I want to discriminate the return type to "if a property in an object exists, return something different".

These are the 2 approaches I try, both don't work.

  1. Using 'select' extends keyof T Playground Link
type Args = {
  select?: boolean
}

interface Search {
  findIt<T extends Args>(a: T): 'select' extends keyof T ? number : boolean
}

function Search(): Search {
  const search = () => null
  search.findIt = <T extends Args>(a: T) => a.select !== undefined ? 1 : false
  return search
}

Error:

Type '{ (): any; findIt<T extends SomeArgs>(a: T): false | 1; }' is not assignable to type 'Search'.
  Types of property 'findIt' are incompatible.
    Type '<T extends SomeArgs>(a: T) => false | 1' is not assignable to type '<T extends SomeArgs>(a: Subset<T, SomeArgs>) => "select" extends keyof T ? number : boolean'.
      Type 'false | 1' is not assignable to type '"select" extends keyof T ? number : boolean'.
        Type 'false' is not assignable to type '"select" extends keyof T ? number : boolean'.ts(2322)
  1. Using T extends ArgsWithSelect Playground Link
type Args = {
  select?: boolean
}

type ArgsWithSelect = {
  select: boolean
}

interface Search {
  findIt<T extends Args>(a: T): T extends ArgsWithSelect ? number : boolean
}

function Search(): Search {
  const search = () => null
  search.findIt = <T extends Args>(a: T) => a.select === undefined ? false : 1
  return search
}

Error:

Type '{ (): any; findIt<T extends SomeArgs>(a: T): false | 1; }' is not assignable to type 'Search'.
  Types of property 'findIt' are incompatible.
    Type '<T extends SomeArgs>(a: T) => false | 1' is not assignable to type '<T extends SomeArgs>(a: T) => T extends SomeArgsWithSelect ? number : boolean'.
      Type 'false | 1' is not assignable to type 'T extends SomeArgsWithSelect ? number : boolean'.
        Type 'false' is not assignable to type 'T extends SomeArgsWithSelect ? number : boolean'

I'm running on [email protected].
Testing both interface definitions on a type level - they work perfectly. The only problem here is that TypeScript is not able to correctly infer the types from the ternary expression in the actual implementation.

In other words, I'm able to express this on a TypeSystem level, but I'm not able to provide an implementation that the compiler accepts.

Playground Link:

First Playground
Second Playground

Related Issues:
#28597

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions