#Type detection

9 messages · Page 1 of 1 (latest)

peak pine
#

I have a type SearchResultTrack, it consists of several other interfaces:

export enum Origin {
    Youtube = "youtube",
    Spotify = "spotify",
    Soundcloud = "soundcloud",
}

export enum SearchResultType {
    Track = "track",
    Playlist = "playlist",
}

export enum QueryType {
    Url = "url",
    Search = "search",
}

interface BaseTrack {
    name: string;
    author: string;
    url: string;
    thumbnail: string;
    estimatedDuration: number;
}

interface PlaylistInfo {
    name: string;
    author: string;
    url: string;
    tracksNum: number;
    thumbnail: string;
}

interface SearchResultTrackBase {
    origin: Origin;
    type: SearchResultType.Track;
    query: {
        text: string;
        type: QueryType;
    };
}

export interface SearchResultUrlTrack extends SearchResultTrackBase, BaseTrack {
    query: {
        text: string;
        type: QueryType.Url;
    };
}

export interface SearchResultUrlPlaylistTrack extends SearchResultTrackBase, BaseTrack {
    query: {
        text: string;
        type: QueryType.Url;
    };
    playlistInfo: PlaylistInfo;
}

export interface SearchResultSearchTrack extends SearchResultTrackBase {
    query: {
        text: string;
        type: QueryType.Search;
    };
    variants: BaseTrack[];
}

export type SearchResultTrack = SearchResultUrlTrack | SearchResultUrlPlaylistTrack | SearchResultSearchTrack;

And a have a function:

function formatTrack(rawTrack: SearchResultTrack) {
    if (rawTrack.query.type === QueryType.Url) {
        console.log(rawTrack.name);
    }
}

The problem is that it throws an error:

Property 'name' does not exist on type 'SearchResultTrack'.
  Property 'name' does not exist on type 'SearchResultSearchTrack'.

I think that I need to have some more advanced type checking, but I don't know how to do it. I tried asking ChatGPT several times with different prompts but I didn't get anything useful.

rotund mason
#

@peak pine Typescript doesn't narrow based on properties in nsted objects.

#

Here's a simplified example:

analog monolithBOT
#
type Simple = 
  | { kind: "A", aVal: string }
  | { kind: "B", bVal: string }

function example(obj: Simple) {
  if(obj.kind === "A") {
    console.log(obj.aVal); // ok
  }
}

type Nested = 
  | { nested: { kind: "A" }, aVal: string }
  | { nested: { kind: "B" }, bVal: string }

function example2(obj: Nested) {
  if(obj.nested.kind === "A") {
    obj.aVal;
//      ^^^^
// Property 'aVal' does not exist on type 'Nested'.
//   Property 'aVal' does not exist on type '{ nested: { kind: "B"; }; bVal: string; }'.
  }
}
rotund mason
#

In the first case it works because obj.kind === "A" changes the type of obj - in the second case it doesn't because obj.nested.kind === "A" doesn't change the type.

#

If you can change the structure so it's flattened, that would make this easier to work with.

#

Otherwise you can try to fix it via writing a custom type-guard:

function isBaseTrack(rawTrack: SearchResultTrack): rawTrack is BaseTrack {
    return /* some boolean */
}
function formatTrack(rawTrack: SearchResultTrack) {
    if (isBaseTrack(rawTrack)) {
        console.log(rawTrack.name);
    }
}
#

But TS won't check custom type-guards, you just have to make sure they're correct yourself; which is a bit tricky here as the thing you're checking: rawTrack.query.type === QueryType.Url seems to only be coincidentally related to the thing you're accessing (a property of BaseTrack)

peak pine
#

Ok, thank you very much for helping me. I thought it would work for nested types.