#How to force TS to infer a generic type argument as a string literal type?

9 messages · Page 1 of 1 (latest)

teal spade
#

My problem is basically perfectly encapsulated in this playground

You can also read the code here:

function foo<T extends number | string = string, U extends string = string>(name: U): U {
  return name
}

const bar = foo("hello")
//    ^? const bar: "hello"

// I want `U` to still be inferred as a string literal,
// even though I just explicitly gave the first generic type argument
const baz = foo<number>("bye!")
//    ^? const baz: string
warm stirrupBOT
#

@teal spade Here's a shortened URL of your playground link! You can remove the full link from your message.

ecvanbrussel#0

Preview:```ts
function foo<T extends number | string = string, U extends string = string>(name: U): U {
return name
}

const bar = foo("hello")
// ^? const bar: "hello"

// I want U to still be inferred as a string literal,
// even though I just explicitly gave the first generic type argument
...```

sand anchor
#

@teal spade TS does not partially infer generic params.

#

If you specify one, then any unspecified params get the default value (hence what = string is doing)

#

If you need to infer one argument and specify another, you can split the function into two functions (one which is specified, the other which is inferred)

#

Something like:

foo<number>()("hello");
#

... but also consider whether the manually specified argument is the best way to handle that: a lot of places where people are manually passing type arguments are basicaly just type casts.

teal spade
#

@sand anchor Ah, hmm, then maybe it's not solvable, I'm not sure.

Of course I gave the simplest repro I could make, but in my actual project I have a class like so:

import { Subject } from "rxjs"

class Source<
  Input extends Event = Event,
  Output = Input,
  Type extends string = string,
> extends Subject<{ type: Type, payload: Output }> {
  type: Type

  constructor(type: Type) {
    super()
    this.type = type
  }

  // and then a whole bunch more functions
}

Long story short, whoever instantiates that Source will most likely always fill in the first generic type argument:

const btnClick$ = new Source<MouseEvent>("btnClick$")

And I would just love if I could somehow get that string given to the constructor to be kept as the literal string. It just helps when hovering over a piece of code way down the line, if the IDE shows you that the type contains the literal "btnClick$" string.

Is there any way to make that work?

#

Should I add a generic type to the contructor function itself maybe?