1

I am getting this error, Where as U is number and returning the number too.

Type 'number' is not assignable to type 'U'.
function foo<T extends string, U extends number>(a: T): U {
    return +a; // error in this line
}


foo<string, number>('12')
Indraraj26
  • 1,726
  • 1
  • 14
  • 29
  • 4
    you are mis-using generics here. Why not just do: `function foo(a: string): number { return +a; }` ? – Tobias S. Nov 02 '22 at 08:21
  • 2
    Deinitely just return a `number` instead of `U`. If the actual function does need to use the generic, you could also cast the return value `return +a as U`. –  Nov 02 '22 at 08:24
  • 1
    to get to the "why the error is happening part": right now, you could call the function with any number literal `foo('12')`. The return type would be `1000` but it actually returns `12`. That's what the compiler is trying to warn you about. – Tobias S. Nov 02 '22 at 08:25
  • it is just an example. I know I can do that without generics. Yes with a cast is working fine. @TobiasS. your example does not make any sense 1000 and return 12 in that case it is perfect but there we are saying it will return number and if is returning that it should not throw an error – Indraraj26 Nov 02 '22 at 08:32
  • 1
    Try [this](https://tsplay.dev/WPpnqW) – captain-yossarian from Ukraine Nov 02 '22 at 08:54

2 Answers2

1

you need to use as keyword to cast the return value type

function foo<T extends string, U extends number>(a: T): U {
    return +a as U;

}

foo<string, number>('12')
Anis
  • 1,190
  • 2
  • 13
  • 26
  • didn't noticed that Mike S have already answered in comment section – Anis Nov 02 '22 at 08:34
  • 1
    A comment is not an answer, don't worry :) I would, however, mention that this allows you to do something like `const a = foo('12')` which is wrong (and that's what the error is tryinfg to tell you). –  Nov 02 '22 at 08:36
1

I believe it worth using overloading here:

type UnsafeNumber = number & { tag?: 'UnsafeNumber' }

type ParseInt<Str extends string> =
  Str extends `${infer Digit extends number}`
  ? Digit
  : UnsafeNumber


function foo<T extends string>(a: T): ParseInt<T>
function foo<T extends string>(a: T) {
  return +a;
}

type Result = ParseInt<'.2'>

const _ = foo('42') // 42
const __ = foo('4.2') // 4.2
const ___ = foo('s03') // UnsafeNumber

Playground

Since TS does not distinguish number and NaN I have provided UnsafeNumber which corresponds to NaN. However, it is still not safe and I would recommend you to use parseItn function

ParseInt utility type

Since TS 4.8, TS is able to infer literal number from stringified number. See docs. It means that TS is able to infer 100 from "100". It means that when you provide "42" as an argument, TS will be able to infer it as a digit 100. If you will provide just a regular string, TS will not be able to do it and false branch in conditional type will be evaluated, or in other words UnsafeType will be returned.

UnsafeType is just a regular number with using of branded type pattern. So TS will be able to distinguish regular number and UnsafeNumber. UnsafeNumber means that TS is not able to infer digit number from string and it might be NaN

If you are interested and type inference and validation you can check my this article about number inference and inference on function arguments in general or this answer