简体   繁体   中英

Typescript type a or type b

I have the 2 following interfaces

interface IComment extends IData {
  comment: string;
}
interface IHistory extends IData{
  differences: any[];
  timeStamp: number;
}

both of them extend

interface IData {
  user: string;
  date: Moment | string;
  isHistory: boolean;
}

now to the problem

I have an Array with elements of IComment and IHistory.

const data: Array<IHistory | IComment> = [...someHistoryArray, ...someCommentArray]

now when I want to map over the array and I want to access the timeStamp

data.map((entry: IHistory | IComment) => {
  if(entry.isHistory) {
    entry.timeStamp 
    // TS2339: Property 'timeStamp' does not exist on type 'IHistory | IComment'. Property 'differences' does not exist on type 'IComment'.
  } else {
    entry.comment
    // TS2339: Property 'comment' does not exist on type 'IHistory | IComment'.   Property 'comment' does not exist on type 'IHistory'.
  }
})

well I found 2 solutions that aren´t satisfing enough for me...

  1. I could write at every position

    (entry as IHistory).timeStamp
  2. I could define for example

    const historyEntry: IHistory = entry as IHistory;

Are there any other possible solutions?

You can use isHistory as a discriminant for the union if you add specific definitions in each interface:

interface IComment extends IData {
    comment: string;
    isHistory: false;
}
interface IHistory extends IData {
    differences: any[];
    timeStamp: number;
    isHistory: true;
}
interface IData {
    user: string;
    date:  string;
    isHistory: boolean;
}

let data: Array<IComment | IHistory>=[]
data.map((entry: IHistory | IComment) => {
  if(entry.isHistory === true) {
    entry.timeStamp //ok 

  } else {
    entry.comment //ok

  }
})

Another possibility would be to use a User-Defined Type Guard, ie a function that helps the compiler to derive whether a parameter has a certain type. The following code should solve your specific issue - i added comments to the changed bits.

interface IComment extends IData {
    comment: string;
}

interface IHistory extends IData {
    differences: any[];
    timeStamp: number;
}

interface IData {
    user: string;
    date: Moment | string;
    isHistory: boolean;
}

const data: Array<IHistory | IComment> = [];

data.map((entry: IHistory | IComment) => {
    // Explicitely narrows down the type to IHistory within the block
    if (isHistory(entry)) {
        // entry.timeStamp
    } else {
        // entry.comment
    }
});

// User-Defined Type Guard
function isHistory(data: IData): data is IHistory {
    return data.isHistory;
}

See Advanced Types at User-Defined Type Guards for more info.

You can use "in" for this case.

if('timeStamp' in entry) {
    entry.timeStamp 
} else {
    entry.comment
  }

Documentation: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM