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

Conditional type doesn't narrow primitive types #26240

Open
sergey-shandar opened this issue Aug 6, 2018 · 7 comments
Open

Conditional type doesn't narrow primitive types #26240

sergey-shandar opened this issue Aug 6, 2018 · 7 comments
Assignees
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@sergey-shandar
Copy link
Contributor

sergey-shandar commented Aug 6, 2018

TypeScript Version: 3.1.0-dev.201xxxxx

Search Terms:

Code

type OnlyNumber<T extends number> = T

type ToNumber<T extends number | string> =
    T extends string ? undefined : OnlyNumber<T>

Expected behavior:
No errors

Actual behavior:
Error:

index.ts:37:47 - error TS2344: Type 'T' does not satisfy the constraint 'number'.
  Type 'string | number' is not assignable to type 'number'.
    Type 'string' is not assignable to type 'number'.

37     T extends string ? undefined : OnlyNumber<T>

Playground Link: https://www.typescriptlang.org/play/#src=type%20OnlyNumber%3CT%20extends%20number%3E%20%3D%20T%0D%0A%0D%0Atype%20ToNumber%3CT%20extends%20number%20%7C%20string%3E%20%3D%0D%0A%20%20%20%20T%20extends%20string%20%3F%20undefined%20%3A%20OnlyNumber%3CT%3E%0D%0A

Related Issues:

@sergey-shandar
Copy link
Contributor Author

sergey-shandar commented Aug 6, 2018

IMHO, this's issue is slightly related to #26199. It looks like TS doesn't properly handle underlying JavaScript types. I mean, each TS type should have two components:

  1. underlying JavaScript type. It's a subset of { boolean, number, null, undefined, object, symbol, function }, aka unknown. See also JS typeof.
  2. interfaces, restriction on properties, member-functions etc.

All type computations should be based on these two independent components.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Aug 6, 2018
@AnyhowStep
Copy link
Contributor

Wouldn't this be perfectly fine if you just did extends number ? OnlyNumber<T> : undefined?

@falsandtru
Copy link
Contributor

Looks like not limited to primitive types.

@AnyhowStep
Copy link
Contributor

What's wrong with this?

type ToNumber2<T extends number | string> =
    //OK
    T extends number ? OnlyNumber<T> : undefined 

@sergey-shandar
Copy link
Contributor Author

@AnyhowStep yes, it's workaround for the bug which works fine for the specific sample case.

@weswigham
Copy link
Member

This is just a manifestation of how we don't track negated constraints in the false branch of a conditional type.

@weswigham weswigham self-assigned this Jan 8, 2019
@flushentitypacket
Copy link

flushentitypacket commented Feb 14, 2019

I think I've run into the same issue but in a different context--just going to add this here to put a snippet in for anyone searching for answers for the same type of problem.

type OptionalFn = (() => string) | undefined
// Type 'OptionalFn' does not satisfy the constraint '(...args: any[]) => any'.
// Type 'undefined' is not assignable to type '(...args: any[]) => any'
type OptionalFnReturnType = OptionalFn extends undefined ? undefined : ReturnType<OptionalFn>

Can workaround by manually narrowing type:

type OptionalFn = (() => string) | undefined
type OptionalFnReturnType = OptionalFn extends undefined ? undefined : ReturnType<NonNullable<OptionalFn>>

(See NonNullable source in the Typescript docs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants