#Union isn't narrowing by typeof?!

1 messages · Page 1 of 1 (latest)

whole musk
#

I'm using typeof foo.bar to discriminate a {bar: string} | {bar: SomeClass, baz: string} union, but TS won't narrow the union type based on the result of the typeof.
Is this expected behavior?

bleak kelpBOT
#
apyh#0

Preview:```ts
class Foo {
foo: number
constructor() {
this.foo = 3
}
}

type Bar =
| {
signer: string
asdf: string
}
| {
signer: Foo
}

function test() {
const token = {
signer: '',
asdf: ''
} satisfies Bar as Bar

if(typeof token.signer === 'string') {
...```

ruby ore
#

performing a check on one property does not change the type of the original object in TS

#

the TS compiler isn't smart enough for that yet, or at least it can't really be proven easily using types, even if it seems logical to us

#

instead, try creating a new variable and using that one

#

also, satisfies Bar as Bar is kinda useless

#

you could/should put the proper type on the variable directly

#

const token: Bar = { ... }

whole musk
whole musk
bleak kelpBOT
#

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

apyh#0

Preview:```ts
class Foo {
foo: number
constructor() {
this.foo = 3
}
}

type Bar =
| {
disc: "x"
signer: string
asdf: string
}
| {
disc: "y"
signer: Foo
}

function test() {
const token = {
disc: "x",
signer: '',
...```

ruby ore
#

typeof token.signer === 'string' makes sense to us
for for TS, you aren't acting on the token object

ruby ore
whole musk
#

right - so i'm wondering why using a discriminator can narrow the type, but a typeof can't, when the result is the same - i'm performing some runtime check to narrow a type

#

is it the indirection of the typeof operation?

ruby ore
#

because by using a discriminator, you actually use exhaustion checking
it must be one of the finite possibilities of the union

bleak kelpBOT
#

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

apyh#0

Preview:```ts
class Foo {
foo: number
constructor() {
this.foo = 3
}
}

type Bar =
| {
disc: "x"
signer: string
}
| {
disc: "y"
signer: Foo
}

function test() {
const token: Bar = {
disc: "x",
signer: "",
}

type BBBB = typeof token.signer
...```

ruby ore
#

however, when not using those, it's just regular narrowing

#

and narrowing a property that way does not impact the parent object

whole musk
whole musk
#

makes sense

ruby ore
ruby ore
#

just that there are different ways to do narrowing

#

some are more appropriate than others in certain cases

whole musk
#

i mean, yeah - discriminants with ===, in, typeof but only on primitives..

#

feels like an outlier that typeof for a discriminant doesn't work

ruby ore
#

it's not really typeof vs ===

whole musk
#

right, cuz it's typeof x === y

#

i get that TS isn't working thru all that logic'

ruby ore
#

it's "equality narrowing of a property" vs "exhaustiveness checking"

#

each type of narrowing have pros and cons

#

sometimes many ways are or look possible

whole musk
#

yeah, fair enough

ruby ore
#

you can alwayse use casts if you are certain a value will be a certain type and the compiler isn't smart enough to narrow it as of now

whole musk
#

i'd mark this solved, then

#

the only thing i have left is that the satisfies.. as.. is actually needed :P

ruby ore
#

really? MeowThink

bleak kelpBOT
#
apyh#0

Preview:```ts
class Foo {
foo: number
constructor() {
this.foo = 3
}
}

type Bar =
| {
disc: "x"
signer: string
}
| {
disc: "y"
signer: Foo
}

function test() {
const token: Bar = {
disc: "x",
signer: "",
}

type BBBB = typeof token.signer
...```

whole musk
#

check it out

#

ts 5.0 tries to be clever

#

and auto-narrows if i just use a type on the var

ruby ore
#

because to me it really looks like it's the same thing as const token: Bar

whole musk
#

nah nah check my example

ruby ore
#

since TS still checks the object meet's Bar requirements to be assigned, and doesn't infer the exact type

whole musk
#

ah, but it does infer the exact type in 5.0 😁

#

or, well, at least narrows it to one union arm

ruby ore
whole musk
#

ain't it just 🤔

ruby ore
#
const token: Bar = {
  disc: "x",
  signer: ''
} as Bar

works

#

you don't really need satisfies, but that's not a big deal

#

still need to type Bar twice