type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
name: string;
age: number;
location: string;
}
// credit to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Values<T> = T[keyof T]
type AllowedStates<T> = {
[Prop in keyof T]: (arg: Prop) => T[Prop]
}
type Overloading<T> = UnionToIntersection<Values<AllowedStates<T>>>
const get: Overloading<Person> = (arg: string) => {
return null as any
}
const result = get('age') // number
const result2 = get('name') // string
const result3 = get('location') // string
Playground
You must dynamically create all allowed states - see AllowedStates
Then retrieve all Values
from AllowedStates
Next, convert the union Values
to intersection - UnionToIntersection
. This is because an intersection of functions produces function overloading.
UPDATE
Is there a way to use that type on a class method instead of an arrow function?
Yes, you don't even need to create any overloads
interface Person {
name: string;
age: number;
location: string;
}
const person = {
name: 'string',
age: 42,
location: 'string',
}
class Overloads<T> {
constructor(public obj: T) { }
get<Key extends keyof T>(key: Key): T[Key] {
return this.obj[key]
}
}
const get = <T,>(obj: T) => <Key extends keyof T>(arg: Key): T[Key] => obj[arg]
{
const getter = get(person);
const x = getter('age') // number
const xx = getter('name') // string
}
const _ = new Overloads(person);
const __ = _.get('age') // number
const ___ = _.get('name') // string
Playground
Syntax for method overloading:
class Overloads {
get(key: 'hello'): string
get(key: 'bye'): number
get(key: string) {
return null as any
}
}
UPDATE
type Person = {
name: string;
age: number;
location: string;
};
class Overloads {
get<Key extends keyof Person>(key: Key): Person[Key]
get<Key extends keyof Person>(key: Key) {
switch (key) {
case 'name': {
return 'string';
}
case 'age': {
return 42
}
case 'location': {
return 'string';
}
default: {
return null
}
}
}
}
const _ = new Overloads();
const __ = _.get('age') // number
const ___ = _.get('name') // string
UPDATE 2
type Person = {
name: string;
age: number;
location: string;
date: Date;
};
type Values<T> = T[keyof T];
type Union<T> = Values<{
[Prop in keyof Person]: [Prop, Person[Prop]]
}>
class Overloads {
set(...args: Union<Person>): void {
if (args[0] === 'date') {
console.log(args[1].getTime());
}
}
}