Reputation: 733
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
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