#Keep only literals from string type

24 messages · Page 1 of 1 (latest)

hollow saddle
#

Given this type

type BobWithString = 'bob' | string

Is it possible to create a type helper which extract all literals except the generic string?

I feel like this is not possible but I can't prove it.

high citrus
#

in this case the type is reduced fully to string, you'd need string & {} to preserve the union at all

hollow saddle
#

I am using a library that I cant edit, which provides me this type. I am stuck then 😦

normal garden
#

what library? that type is probably pointless

high citrus
spice hedgeBOT
#
that_guy977#0

Preview:ts ... type WithoutString<T extends string> = T extends T ? string extends T ? never : T : never ...

high citrus
hollow saddle
#

Corporate library - I dont want to go in details about it - I hoped we could have some magic trick in typescript.

But ya, the type is useless, I will sweat a bit to have it changed 😬

#

Thank you all 🙂

sonic patrol
#

I see it a lot but I don’t get its reason for it being there. Wouldn’t it always be true (aka T does extend T)?

warm pine
#

@sonic patrol It makes it a distributive conditional

#
type Box<T> = { value: T };
type BoxDistributive<T> = T extends T ? { value: T } : never;

type Example = Box<string | number> // { value: string | number }
type Example2 = BoxDistributive<string | number>; // { value: string } | { value: number }
spice hedgeBOT
hollow saddle
#

The distributive thing, everytime i read about it, everytime i squint 😄

warm pine
#

Yeah; it's one of the stranger parts of TS behavior.

#

It's useful but pretty unintuitive in how it's triggered.

high citrus
hollow saddle
#

I will try to recall the precise use-case, but I rememver vaguely one time where distributive behavior was not wanted and we had to find hacks to prevent it. this is when i read about it actually 😄

warm pine
#

Yeah, you run into that occasionally and end up doing something like [T] extends [OtherThing] to prevent it.

#

Sort of a classic example, trying to write an IsNever utility requires preventing the distrubtion because never is treated like an empty union:

type IsNever_Broken<T> = T extends never ? "Yes" : "No";
type Test1 = IsNever_Broken<never>; // Expected "Yes", but got never, because it distributed the never

type IsNever<T> = [T] extends [never] ? "Yes" : "No";
type Test2 = IsNever<never>; // Got "Yes"
hollow saddle
#

This is why ts-toolbelt is so complex to look at

sonic patrol
#

So basically T extends T ? T : never and [T] are opposites of each other. The T extends T distributes the union in T and [T] keeps it together. Is that right?

high citrus
#

any T extends ... where T is dynamic (generic or inferred) is distributed.
[T] extends ... isn't in this form, so it isn't distributed