#How to "Extract" a narrowed type from another type?

1 messages · Page 1 of 1 (latest)

warm berryBOT
#
lifeiscontent#0

Preview:```ts
type Test = {bool: boolean}

type Foo = Extract<Test, {bool: false}>

// how to get {bool: false} from {bool: boolean}?```

alpine carbon
#

are you looking for an intersection?

fierce solar
#

this is a contrived example, the code I was working with also had a type, e.g.

Test = { bool: boolean, type: "foo" | "bar" | "baz" }

and the second type parameter would of been {bool: false, type: "foo"}

warm berryBOT
#
that_guy977#0

Preview:```ts
type Test = {bool: boolean}

type Foo = Expand<Test & {bool: false}>
// ^?

// how to get {bool: false} from {bool: boolean}?

type Expand<T> = {[K in keyof T]: T[K]}```

alpine carbon
#

Extract will only yield members of the input union

fierce solar
#

oh, this is pretty neat, is there a opposite of Expand like Exclude for Extract?

alpine carbon
#

Exclude is just Extract with inverted logic, unrelatd to Expand

#

Expand doesn't have any logic, it's just a transformation to get ts to, as the name suggests, expand a type

#

the actual logic is just Test & { bool: false } here

fierce solar
#

yep, do you know how to do a reverse of the Expand helper you've shown here?

alpine carbon
#

wdym reverse?

#

it's not doing anything

fierce solar
#

sure, let me put a playground together

alpine carbon
#

expand just doesn't do anything to the type, there's no operation to reverse

warm berryBOT
#
lifeiscontent#0

Preview:```ts
export function isNotUnknownTypeColumn(
field: V2.Column
): field is Omit<V2.Column, "type"> & {
type: Exclude<V2.Column["type"], "unknown">
} {
return field.type !== "unknown"
}

export function isNotUnknownTypeAndPrimaryKeyColumn(
field: V2.Column
): field is Omit
...```

alpine carbon
#

you probably don't need those Omits

fierce solar
#

what would you suggest?

alpine carbon
#

removing them

fierce solar
#

that's it?

alpine carbon
#

that's all unrelated to Expand though

alpine carbon
fierce solar
#

yeah, I'm just looking at this:

Omit<Column, "type" | "is_primary_key"> & {
  type: Exclude<Column["type"], "unknown">;
  is_primary_key: true;
}```

and its not exactly easy to read for the newbie
alpine carbon
#

is Column a DU?

fierce solar
#

DU?

alpine carbon
#

a discriminated union

fierce solar
#

nope

#

it could be

#

but I thought it was pointless because the values besides the type are all the same

alpine carbon
#

oh is it your own type?

fierce solar
#

yes

alpine carbon
#

i thought this was from a library

fierce solar
#

it should be

#

lol

alpine carbon
fierce solar
#

our backend APIs don't change much so its currently not a big deal

alpine carbon
#

it just seems like it'd be a DU with that type, and if you had that Extract/Exclude would work on it directly

#

but yeah you'll just have to use intersections

#

you don't have to omit before the intersection though, since you're always narrowing

#

it'd be needed if you were overwriting with an unrelated/wider type

fierce solar
#

yeah, wondering if I should make a helper to turn things into DU based off a key or something, but not sure if it feels more like bikeshedding

#

I see, so when you merge with a narrowed type the narrowed type is preferred?

alpine carbon
#

if you needed a DU you wouldve written one, you probably don't need a helper for that very broad task

alpine carbon
#

do you know set theory, from math?

fierce solar
#

nope

#

I have a rough idea

#

but not well enough to explain it

alpine carbon
#

try going through the wikipedia of that a bit

#

you might see "union" and "intersection" there

fierce solar
#

I know what unions and intersections are

alpine carbon
#

ts' type system is based on set theory, where types are sets and values are possible elements within those sets
unknown, the top type, is the universe set, containing all elements; all values are valid unknowns
never, the bottom type, is the empty set, containing no elements; no values are valid nevers

#

etc, etc

fierce solar
#

so what you're saying is in TS, you're not merging but rather limiting a set of some form

alpine carbon
#

a narrower type is a smaller set, a subtype is a subset

fierce solar
#

yep

#

ok got it

alpine carbon
#

if A is a subset of B, A intersect B is A, A union B is B

#

ts has that same logic

fierce solar
#

okay, so based off that

#

I guess what I'm wondering is how to create a subset of object types {type: 'a' | 'b' |'c', bool: boolean} how can I limit this set to {type: 'a', bool: false}

alpine carbon
#

by intersection :)

fierce solar
#

would you use Expand for that?

alpine carbon
#

no, again, Expand does nothing

#

it just makes it look pretty with hover

fierce solar
#

haha

alpine carbon
#

it doesn't do any processing

fierce solar
#

is & an intersection? or |?

alpine carbon
#

& is the intersection operator, | is the union operator

fierce solar
#

I see

#

so if I did A(widerType) & B(subType) that would limit the result?

alpine carbon
#

it would result in B, yeah

fierce solar
#

I see

#

thank you, that's really helpful

#

I appreciate you taking the time to share

alpine carbon
#

intersections are analogous to "and", unions are analogous to "or" (hence the operators)
string | number => string or number
{ a: number } & { b: string } => property a is number and property b is string ({ a: number; b: string })

#

so in your case, Column & { type: "unknown" } => is valid column (has all the column properties), and has property type: "unknown"

#

(i mean, in your context. in your case, it's all the types except "unknown")

fierce solar
#

yep, I was aware of that much, but the thing that I think was missing was that types are sets, that was new to me.

#

anyway, thanks again 🙂