So I have a string union type
type S = 'a' | 'b' | 'c';
I want to initialize a value of type S, while asserting that is of type S. With a variable, it's very easy:
const s1: S = 'a';
const s2: S = 'z'; // error
However, I just want to instantiate the value itself, along the lines of 'a' as S but checking that 'a' is of type S.
The exact use case is that I'm using a library that provides a function f(s: string), but I want to ensure that when I call it, I only call it with the strings I deem ok. Since [afaik] you can't narrow the signature from outside the library, I was thinking of doing something like f('a' as S) every time I used f.
Defining my own function g = (s: S) => f(s) isn't a great option because actually f's signature looks like f(s: string, t: T) where T is a complex, library defined type that I'm not sure is even exported.
What would be the best way to accomplish this?
Wrap the external function with your own using the string type (TS playground):
const externalFn = <T>(s: string, t: T) => t
type S = 'a' | 'b' | 'c';
const yourFn = (s: S, t: Parameters<typeof externalFn>[1]) => externalFn(s, t)
If you need to handle a function with more than one argument, you can use the idea in this answer (TS playground):
type DropFirst<T extends unknown[]> = T extends [any, ...infer U] ? U : never
const externalFn = <T>(s: string, t: T) => t
type S = 'a' | 'b' | 'c';
const yourFn = (s: S, ...rest: DropFirst<Parameters<typeof externalFn>>) => externalFn(s, ...rest)
I ended up making a simple wrapper function like so:
function ok<T extends string>(s: T): T {
return s;
}
that I could then use like f(ok<S>('a')), because in my case there were multiple functions and types that I wanted to do this verification for, and making a separate wrapping function would be tedious and not maintainable.