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
5
  • use the unknown keyword. This will force the next user to apply a type when it is not available, pls refer : stackoverflow.com/questions/51439843/unknown-vs-any for more details Commented Jul 21, 2021 at 11:17
  • @SamridhTuladhar I don't want to force user to apply type Commented Jul 21, 2021 at 11:19
  • getName expects number but you want to pass a string Commented Jul 21, 2021 at 11:27
  • @captain-yossarian no I don't and it's doesn't matter, question is about getSmth Commented Jul 21, 2021 at 11:28
  • 2
    The signature <T extends string | null = string | null>(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. Commented Jul 21, 2021 at 11:50

1 Answer 1

3

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);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.