#How to distinguish between types when defining multiple on a function param
49 messages · Page 1 of 1 (latest)
To add -- The only difference between Listing and FeedItem is that FeedItem extends Listing and adds an owner property
I have this:
async (item:string | Listing | FeedItem) => {
let listing
if (typeof item === 'string') {
const fetched = await getDoc(doc(listings, item)).then(i => i.data())
if (fetched) listing = {...fetched}
} else {
listing = item
}
if (!listing) return
const foo = listing
}
But for some reason foo is typed as only Listing instead of both FeedItem and Listing
The getDoc call returns Listing | undefined hence the check
Feel like I am blanking out because this should be simple
Switched to a discriminated type:
type UpdateParams = {
type: 'id';
item: string;
} | {
type: 'listing';
item: Listing;
} | {
type: 'feedItem';
item: FeedItem
}
But this just seems... extra? Would be interested in a better way (if there is one)
there is nothing specific to TypeScript really
you'd do it the exact same way in JS:
comparing two raw objects
checking if those have some properties set, etc.
there is a list with all the possible way you can tell different values appart
that concept is called narrowing
!hb narrowing
in your case, you could simply use the JS in operator, to see if a property is present on the object or not
if it is, then you (and TS) know it's an object of such type
and if not, then the other type
@winged grove
but using a discriminator (extra property in commo with unioque value for each type) (what you did) is another possible way of doing things
When testing using in is it common to have to cast the base type to the value after?
wdym exactly? 
Because I can check for 'owner' in listing, but it doesn't infer the FeedItem type from that, even though it is the only possible option
think it should work
Preview:```ts
interface Listing {
id: string
owner: string
foo: string
}
interface FeedItem {
id: string
bar: string
}
declare const element: Listing | FeedItem
if ("owner" in element) {
element.foo // is of type Listing, can access unique property "foo
...```
!ts
interface Listing {
id: string;
owner: string;
foo: string;
}
interface FeedItem {
id: string;
bar: string;
}
declare const element: Listing | FeedItem;
if("owner" in element) {
element.foo; // is of type Listing, can access unique property "foo"
} else {
element.bar; // is of type FeedItem, can access unique property "bar"
}
Can you explain this?
what part exactly?
also, what's the error?
(maybe you could post the code in the TS playground and share the link)
Look at the type of foo in the if scope, then the type of bar in the function scrope
oh, yeah, it's normal
the scope is different
you have no guarantee the object is still the same
lol wut
that lambda could be called at any point in time
and the check might not be relelvant anymore
tldr. new scope invalids previous checks
If I assign it to its own variable inside the if check it stays the same in the new scope?
in practice, yes
but in the type-system, no, there is no guarantee
TS doesn't know when that function will run
no guarantee the object will still be the same
Sorry, I know screenshots aren't ideal, but it is the easiest way to share what I'm seeing in my IDE. BUT:
That guarantee is there when I assign it to a variable? I just don't understand the difference between that and just using listing outright
interface Listing {
id: string;
owner: string;
foo: string;
}
interface FeedItem {
id: string;
bar: string;
}
declare const listing: FeedItem | Listing;
const fn = () => {
listing;
// ^?
};
!ts
interface Listing {
id: string;
owner: string;
foo: string;
}
interface FeedItem {
id: string;
bar: string;
}
declare const listing: FeedItem | Listing;
const fn = () => {
listing;
// ^? - const listing: FeedItem | Listing
};
tells the compiler a variable exists with such type
but no need to provide actual implementation
(only/mainly used in playground, so you don't need to recreate and initiliaze everything)
(also in typings file where you tell TS those objects exist)
ahh
it's hard to explain and make a good example to show it
but it's "an issue" with scoping, or rather, that's how scoping works