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

Contextual typing with optional discriminant #31618

Closed
matt-tingen opened this issue May 28, 2019 · 3 comments
Closed

Contextual typing with optional discriminant #31618

matt-tingen opened this issue May 28, 2019 · 3 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@matt-tingen
Copy link

matt-tingen commented May 28, 2019

TypeScript Version: 3.5.0-dev.20190525

Search Terms: discriminated union, optional discriminant, contextual typing, missing vs undefined

Opened from SO question since this seems like a possible bug.

TypeScript supports using an explicit undefined for the value of an optional discriminant and contextual typing works with that, but contextual typing does not fully work when an optional discriminant is missing.

Code

interface NumProps {
  isString?: false
  onChange: (value: number) => void
}

interface StringProps {
  isString: true,
  onChange: (value: string) => void
}

type Props = NumProps | StringProps

const props: Props = {
  // Error: Parameter 'value' implicitly has an 'any' type.
  // Expected: 'value' is contextually typed as 'number'.
  onChange: value => {},
}

const propsUndefined: Props = {
  isString: undefined,
  // OK: 'value' is contextually typed as 'number'. 
  onChange: value => {},
}

Expected behavior: value is contextually typed as number for props

Actual behavior: value is any

Playground Link: Playground (enable strict options)

As pointed out in the SO comments, annotating value with an invalid type will error which suggests that TS is correctly resolving the discriminated union, but possibly not applying contextual typing to onChange at the right time.

const props: Props = {
  // Error: Type 'number' is not assignable to type 'boolean'.
  onChange: (value: boolean) => {}
}

Related Issues:
#16817
#13195 (Missing vs undefined props)

@matt-tingen matt-tingen changed the title Contextual typing with Optional discriminant differs from optional discriminant Contextual typing with optional discriminant May 28, 2019
@matt-tingen matt-tingen reopened this May 28, 2019
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label May 28, 2019
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone May 28, 2019
@NiGhTTraX
Copy link

Hitting the same exact problem when trying to improve the types for react-select: Playground Link.

It appears that we can work around it with overloads, but then exporting the type for the component is difficult due to #32164.

import React from 'react';

type SingleProps = { isMulti?: false; onChange: (value: number) => void };
type MultiProps = { isMulti: true; onChange: (value: number[]) => void };

function Select(props: SingleProps): JSX.Element;
function Select(props: MultiProps): JSX.Element;
function Select(props: SingleProps | MultiProps): JSX.Element {
  return <span>foo</span>;
};

const x = [
  <Select onChange={value => console.log(value)} />,
  <Select isMulti={false} onChange={value => console.log(value)} />,
  <Select isMulti onChange={value => console.log(value)} />,
];

Playground Link

@asterikx
Copy link

Still not working in TS 4.2.4 😕

@david10sing
Copy link

It seems to be working on the TS playground now. The type of value is inferred correctly. However, if you add an optional property on B, and you add that property to props, there is no error which I think is incorrect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants