简体   繁体   中英

Typescript Interface - make type of one property dependent of type of another property

I have some Interfaces which look like this:

export enum SortValueType {
  String = 'string',
  Number = 'number',
  Date = 'date',
}


export interface SortConfig {
  key: string;
  direction: SortDirection;
  type: SortValueType;
  options?: {
    sortBy?: 'year' | 'day';
  };
}

I would like to make it possible to extend this so that the possible types of options.sortBy should be dependent on the type . I am creating iteratees depending on type so it should not be possible to create an Object where type is string and options.sortBy has a value of year .

This is the getIteratee function:

 private getIteratee(config: SortConfig) {
    if (config.type === SortValueType.String) {
      return item => _lowerCase(item[config.key]);
    }

    if (config.type === SortValueType.Date) {
      const sortBy = _get(config, 'options.sortBy') as 'year' | 'day';

      if (!!sortBy && sortBy === 'year') {
        return item =>
          !!item[config.key]
            ? new Date(item[config.key]).getFullYear()
            : undefined;
      }

      if (!!sortBy && sortBy === 'day') {
        return item =>
          !!item[config.key]
            ? new Date(item[config.key].toJSON().split('T')[0])
            : undefined;
      }
    }

    return config.key;
  }

I am also open to more generic solutions

I think you want something like this:

export enum SortValueType {
  String = 'string',
  Number = 'number',
  Date = 'date',
}

type SortDirection = string;

export interface SortConfig<T extends SortValueType> {
  key: string;
  direction: SortDirection;
  type: T;
  options:
    T extends SortValueType.Date
      ? { sortBy: 'year' | 'day'; }
      : { sortBy?: undefined };
}
// OK:
let dateConfig: SortConfig<SortValueType.Date> = {
  key: "key",
  direction: "left",
  type: SortValueType.Date,
  options: { sortBy: 'year' }
}

// Not OK:
let numberConfig: SortConfig<SortValueType.Number> = {
  key: "key",
  direction: "left",
  type: SortValueType.Number,
  options: { sortBy: 'year' }
}

Playground link

The sortBy?: undefined is required, because TypeScript doesn't block extra properties by default. If you just use {} then { sortBy: 'year' } is actually valid, as it is assignable to {} .

You can tweak the exact type of options from there, depending on how exactly you want each type's options to work.

To make getIteratee work nicely, you'll want to change the parameter to SortConfig<any> , define a type guard, and use that to narrow down the type for each case. Like so:

function isConfigType<T extends SortValueType>(
    config: SortConfig<any>, type: T
): config is SortConfig<T> {
  return config.type === type;
}

function getIteratee(config: SortConfig<any>) {
    if (isConfigType(config, SortValueType.String)) {
        // The type of 'config' here is SortConfig<SortValueType.String>;
    } ...
}

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