Home > other >  How can I get the correct type if an object property has a prefix and I only know the prefix in TS?
How can I get the correct type if an object property has a prefix and I only know the prefix in TS?

Time:11-08

This one is a bit tricky to describe in words, so I will just start with the code. Let's say I have to deal with strings that have the following format:

type ColumnName<
  Prefix extends string,
  Period extends 'month' | 'week'
> = `${Prefix}.${Period}`;`

For example, ColumnName<'dimension_date', 'month'> would be 'dimension_date.month'. Now let

type Month<Prefix extends string> = ColumnName<Prefix, 'month'>;
type Week<Prefix extends string> = ColumnName<Prefix, 'week'>;

Next, I have a function that takes either a Month or a Week, and a Prefix:

const get = <Prefix extends string>(
  column: Month<Prefix> | Week<Prefix>,
  prefix: Prefix
) => {
    const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

    if (column === monthColumn) {
      console.log(column); // column has type `${Prefix}.month`, as expected
    }
  };

This works as expected. However, I actually need column to be an object property:

type MonthObject<Prefix extends string> = { [K in Month<Prefix>]: number };
type WeekObject<Prefix extends string> = { [K in Week<Prefix>]: number };

const getObj = <Prefix extends string>(
  object: MonthObject<Prefix> | WeekObject<Prefix>,
  prefix: Prefix
) => {
  const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;

  if (monthColumn in object) {
    console.log(object); // object has type MonthObject<Prefix> | WeekObject<Prefix>, while it expect it to only have type MonthObject<Prefix>.
  }
}

Is there a way to make typescript understand that inside of the if statement the type can only be MonthObject<Prefix>?

CodePudding user response:

You can use a function to assert the type.

const isMonth = 
    <Prefix extends string>(obj: MonthObject<Prefix> | WeekObject<Prefix>, prefix: Prefix)
        : obj is MonthObject<Prefix> => {
            const monthColumn: ColumnName<Prefix, 'month'> = `${prefix}.month`;
            return monthColumn in obj
}

And use this function as the condition.

const getObj = <Prefix extends string>(
  object: MonthObject<Prefix> | WeekObject<Prefix>,
  prefix: Prefix
) => {
  if (isMonth(object, prefix)) {
    console.log(object);
    //          ^? (parameter) object: MonthObject<Prefix>

  }
}

link to playground

  • Related