[FIXED] Type Alias as Discriminating Union

Issue

I’ve written a fairly complex definition file to match the behavior of an existing JavaScript library

export declare interface XcodeBaseFileTreeObject {
    path: string;
    includeInIndex?: 0 | 1;
    usesTabs?: 0 | 1;
    indentWidth?: number;
    tabWidth?: number;
    wrapsLines?: 0 | 1;
}

export declare interface XcodeUngroupedFileTreeObject extends XcodeBaseFileTreeObject {
    sourceTree: `"<absolute>"` | "SOURCE_ROOT" | "DEVELOPER_DIR" | "BUILT_PRODUCTS_DIR" | "SDKROOT";
    name: string;
}

export declare interface XcodeGroupedFileTreeObject extends XcodeBaseFileTreeObject {
    sourceTree: `"<group>"`;
}

export declare type XcodeAbstractFileTreeObject = XcodeGroupedFileTreeObject | XcodeUngroupedFileTreeObject;

export declare type PBXBaseFileReference = XcodeAbstractFileTreeObject & {
    readonly isa: "PBXFileReference";
    fileEncoding?: 4 | 10;
    lineEnding?: 0 | 1 | 2;
    languageSpecificationIdentifier?: string;
    xcLanguageSpecificationIdentifier?: string;
    plistStructureDefinitionIdentifier?: string;
};

export declare type XcodeFileType = "archive.ar" | "archive.asdictionary" | "archive.binhex";

export declare type PBXInferredFileReference = PBXBaseFileReference & {
    lastKnownFileType: XcodeFileType;
};

export declare type PBXExplicitFileReference = PBXBaseFileReference & {
    explicitFileType: XcodeFileType;
};

export declare type PBXFileReference = PBXInferredFileReference | PBXExplicitFileReference;

Some union types as properties have been shortened for brevity, but the gist here is that I have a discriminating union from which another type is meant to inherit from, and in turn, another discriminating union is meant to inherit from that type. When I use the union at the bottom of the tree in a function like this:

function getFileType(file: PBXFileReference): XcodeFileType {
    return file.lastKnownFileType ?? file.explicitFileType;
}

I get an error that neither lastKnownFileType nor explicitFileType exist on PBXFileReference. In fact, neither property even comes up in the autocomplete when I’m writing that. I was expecting that to act as a typical discriminating union, where if one of those values isn’t defined, it falls back on the other one definitely existing. How can I achieve the structure I’m looking for?

Solution

You can use a ternary expression and use in to check for the property before accessing it:

function getFileType(file: PBXFileReference): XcodeFileType {
  return 'lastKnownFileType' in file ? file.lastKnownFileType : file.explicitFileType;
}

TypeScript playground

Answered By – Oblosys

Answer Checked By – Pedro (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published