Typescript: Conditional type doesn't narrow primitive types

Created on 6 Aug 2018  路  7Comments  路  Source: microsoft/TypeScript


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:

In Discussion Suggestion

Most helpful comment

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

All 7 comments

IMHO, this's issue is slightly related to https://github.com/Microsoft/TypeScript/issues/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.

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

Looks like not limited to primitive types.

What's wrong with this?

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

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

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

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)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rwyborn picture rwyborn  路  210Comments

OliverJAsh picture OliverJAsh  路  242Comments

Gaelan picture Gaelan  路  231Comments

xealot picture xealot  路  150Comments

sandersn picture sandersn  路  265Comments