0

Typescript sandbox.

Sometimes based on code I know what return value will be and want to specify it using:

getSmth<string>(1)

and not

getSmth(1) as string

but not sure how to do it correctly


  1. Problem. Why there is errors if i extend and return correctly?
Type 'null' is not assignable to type 'T'.
  'null' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | null'.(2322)

Example

const getName = (id: number) : string | null => null

const getSmth = <T extends string | null = string | null>(id: number | null): T => {
  if (!id) {
    return null;
  }
  return getName(id);
};

const x1 = getSmth(1) // should be string | null
const x2 = getSmth<null>(1) // should be null
const x3 = getSmth<string>(1) // should be string

  1. Question. Why this assertion happening?
const getSmth2 = <T extends string | null = string | null>(id: number | null): T => {
  if (!id) {
    return null as T;
  }
  return getName(id) as T;
};

const y1: string = getSmth2(1) // why getSmth2 asserts to string when return type should bestring | null
ZiiMakc
  • 31,187
  • 24
  • 65
  • 105
  • use the `unknown` keyword. This will force the next user to apply a type when it is not available, pls refer : https://stackoverflow.com/questions/51439843/unknown-vs-any for more details – tsamridh86 Jul 21 '21 at 11:17
  • @SamridhTuladhar I don't want to force user to apply type – ZiiMakc Jul 21 '21 at 11:19
  • `getName` expects number but you want to pass a `string` – captain-yossarian from Ukraine Jul 21 '21 at 11:27
  • @captain-yossarian no I don't and it's doesn't matter, question is about getSmth – ZiiMakc Jul 21 '21 at 11:28
  • 2
    The signature `(id: number | null): T` makes very little sense; it means I can call `getSmth<'foobar'>(5)` and the emitted Javascript code - in which the string `'foobar'` is not even present - is supposedly guaranteed to return `'foobar'` (or raise an exception). And `getSmth<'baz'>(5)` is supposed to return the string `'baz'` despite compiling to the exact same Javascript code. The compiler is telling you your code is wrong because your code *is* wrong. – kaya3 Jul 21 '21 at 11:50

1 Answers1

2

Why there is errors if i extend and return correctly?

T is not guaranteed to be nullable due to the constraint. For example, string extends string | null:

// true
type X = string extends string | null ? true : false;

So while the default you give to T is nullable, the compiler cannot ensure that this is true for all possible Ts. In fact, that's the very thing you're trying do do: passing string. But once T = string, returning null wouldn't be valid anymore.

You can type-assert in your implementation to make it work. This just shuts up the compiler about something that it is indeed correct about, though, but if that is what you want to do:

const getSmth = <T extends string | null = string | null>(id: number | null): T => {
  if (!id) {
    return null as T;
  }

  return getName(id) as T;
};
  1. Question. Why this assertion happening?

Essentially, it's the same issue. The added point here is that the compiler uses the declared type on the left to infer the generic type argument:

// T inferred to be string
const y1: string = getSmth2(1);

// T is inferred to be string | null
const y2: string | null = getSmth2(1);

You're expecting T to use the default, which happens in this case:

// string | null
const y3 = getSmth2(1);
Ingo Bürk
  • 19,263
  • 6
  • 66
  • 100