exk0730
exk0730

Reputation: 733

Typescript - specify "key" type where Object[key of Object] MUST return string[]

I need a Type that refers to a specific property on an object which is a string[].

interface Example {
  hello: number;
  world: string;
  target: string[];
}

const e: Example = {
  hello: 0, world: 1, target: ['this', 'is', 'annoying']
};

function join<O>(value: string[] | O, key?: keyof O): string {
  // value[key] is invalid because Typescript can't infer that value[key] is string[].
  const toJoin: string[] = Array.isArray(value) ? value : value[key];
  return value.join(',');
}

const csv: string = join<Example>(e, 'target'); // 'this,is,annoying'

What Type does "key" need to be in order to infer that it HAS TO be target and CANNOT be world or hello?

If it's possible without using unknown or any anywhere, please let me know. Thanks!

Upvotes: 0

Views: 107

Answers (1)

Rubydesic
Rubydesic

Reputation: 3476

Your code has a lot of errors in it, however I think this is what you're looking for:

interface Example {
    hello: number;
    world: string;
    target: string[];
    target2: string[]
}

const e: Example = {
    hello: 0, world: "1", target: ['this', 'is', 'annoying'], target2: []
};

// Get a union of the keys of BaseType  whose values are assignable to ValueType
// e.g. PickTypes<Example, string[]> => "target" | "target2"
type PickTypes<BaseType, ValueType> = {
    [Prop in keyof BaseType]: BaseType[Prop] extends ValueType ? Prop : never;
}[keyof BaseType];

function join<O>(value: string[] | O, key: PickTypes<O, string[]>): string {
    const toJoin: string[] = Array.isArray(value) ? value : value[key] as unknown as string[];
    return toJoin.join(',');
}

const csv: string = join<Example>(e, 'target2'); // 'this,is,annoying'

It does need casting as typescript is too stupid, but it's 100% typesafe.

Upvotes: 1

Related Questions