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:
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:
boolean, number, null, undefined, object, symbol, function }, aka unknown. See also JS typeof.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)
Most helpful comment
This is just a manifestation of how we don't track negated constraints in the
falsebranch of a conditional type.