#How to determine object type by propery.startsWith()?

23 messages · Page 1 of 1 (latest)

crimson oracle
#

Basically I'm wondering why does this example doesn't work. Only Image type has name property starting with image/ so why is the type inside if block not asserted?

strange schoonerBOT
#
matixyo#0

Preview:```ts
type Image = {
name: image/${string}
width: 123
}

type Video = {
name: video/${string}
}

type Media = Image | Video

const a = {} as Media

if (a.name.startsWith("image/")) {
console.log(a.width)
}```

calm fossil
#

i don't think template strings can be used as discriminators

crimson oracle
calm fossil
#

huh, guess im wrong

#

startswith just doesn't narrow i guess

#
function startsWith<const T extends string>(str: string, substr: T): str is `${T}${string}` {
  return str.startsWith(substr);
}

declare const x: { type: `image/${string}`, width: number } | { type: `video/${string}` }
if (startsWith(x.type, "image")) {
  x
//^?
  x.type
//  ^?
}
#

!ts

#

!ts

#

!ts

strange schoonerBOT
#
function startsWith<const T extends string>(str: string, substr: T): str is `${T}${string}` {
  return str.startsWith(substr);
}

declare const x: { type: `image/${string}`, width: number } | { type: `video/${string}` }
if (startsWith(x.type, "image")) {
  x
//^? - const x: {
//    type: `image/${string}`;
//    width: number;
//} | {
//    type: `video/${string}`;
//}
  x.type
//  ^? - (property) type: `image/${string}`
}
calm fossil
#

idk im on mobile right now so im not in a position to really thoroughly investigate this

crimson oracle
#

That should work @calm fossil but I was wondering about native support

whole finch
#

that doesn't work, though? check the type of x in the last code sample

#

it seems like narrowing the discriminated union itself doesn't happen here, but i'm not totally sure why

#

if by "native support" you mean augmenting the lib.d.ts type for startsWith, i think this would be safe/correct (but please make sure i didn't overlook something):

strange schoonerBOT
#
mkantor#0

Preview:ts interface String { startsWith<P extends string>( searchString: P, position?: 0 | undefined ): this is `${P}${string}` startsWith( searchString: string, position?: number | undefined ): boolean } ...

whole finch
#

but notice that it still doesn't narrow a the way you want

#

even this super-simple case doesn't work:

strange schoonerBOT
#
declare const isA: (value: string) => value is 'a'

declare const thing: { name: 'a', x: 1 } | { name: 'b', x: 2 }
if (isA(thing.name)) {
  thing.x
//      ^? - (property) x: 1 | 2
}
whole finch
#

i guess type predicates applied to discriminant properties just don't narrow discriminated unions? surprised i never noticed that before

#

i think you'll have to write your own type guard that takes the whole object as an argument instead of just the name property, like so: