Typescript object with conditional values based on key

Viewed 1043

I'm trying to define a type for an object:

const width: Rule = {
  // regular keys with any name
  full: "100%",
  half: "50%",

  // special keys
  $px: [10, 50, 100],
  $rem: [1, 1.5, 2],
  $em: [1, 1.5, 2],
}

Where Array<number> value can only be used with special keys ('$px' | '$em' | '$rem') and string value only with regular ones.

So I tried something like this:

export type Special = "$px" | "$em" | "$rem";

export type Rule<K extends string> = {
  [key: K]: K extends Special ? Array<number> : string;
} 

But having an error at [key: K]:

(parameter) key: K extends string
An index signature parameter type must be either 'string' or 'number'.ts(1023)

What am I doing wrong?

UPDATE:

The answer proposed in the comments doesn't work in my case, because I don't have a declared interface with known keys. I have a Record<string, string | Array<number>> instead and doing something like:

export type Rule = {
  [K in keyof Record<string, any>]: K extends Special ? Array<number> : string;
};

gives an error on special keys:

(property) $px: number[]
Type 'number[]' is not assignable to type 'string'.

So far I ended up with something like this:

export type Rule = Record<string, string | Array<number>> & 
                   Partial<Record<Special, Array<number>>;

But obviously it allows to set Array<number> on regular keys which is not ideal.

0 Answers
Related