Description
Search Terms
new syntax, advanced types, generic, array of arguments
Suggestion
Hi, everybody!
I think it would be cool to create a solution to this problem.
If you want to control the typing of the result of a function based on an unlimited number of incoming arguments, you will need a huge number of overloads.
Use Cases
For example, you have a class that contains a property configuration:
class PropertyConstructor<T> {
type: T;
}
class PropertyConfiguration<T, S extends keyof any> {
constructor(
public propertyKey: S,
public propertyConstructor: new () => PropertyConstructor<T>,
) {}
}
Next, create several inheritors of constructors:
class StringPropertyConstructor extends PropertyConstructor<string> {
}
class NumberPropertyConstructor extends PropertyConstructor<number> {
}
Next, create function, that will build class with property configs:
type Join<T, S extends keyof any> = {[P in S]: T};
function CreateClass<T1, S1 extends keyof any>(p1: PropertyConfiguration<T1, S1>): new () => Join<T1, S1>;
function CreateClass<T1, S1 extends keyof any, T2, S2 extends keyof any>(
p1: PropertyConfiguration<T1, S1>,
p2: PropertyConfiguration<T2, S2>,
): new () => Join<T1, S1> & Join<T2, S2>;
// ... And so on. It's very uncomfortable and ugly.
function CreateClass(...properties: Array<PropertyConfiguration<any, keyof any>>): new () => any {
class NewClass {}
// Here was the logic of executing property constructors
return NewClass;
}
And create some instances of PropertyConfiguration:
const property1 = new PropertyConfiguration('property1', StringPropertyConstructor);
const property2 = new PropertyConfiguration('property2', NumberPropertyConstructor);
Funnaly, Let's try to apply it:
class A extends CreateClass(property1, property2) {
}
const a = new A();
a.property1 // string;
a.property2 // number;
It's works! But, very uncomfortable and ugly. For example if you want to create class with ten properties, you need to implement corresponded overloads.
Examples
As I see the solution to this problem:
type Join<T, S extends keyof any> = {[P in S]: T};
type Union<T extends Array<any>> = ...
/*
built-in type for compare array types.
Example:
Union<[string, number]> => string & number
Union<[Join<string, 'property1'>, Join<number, 'property2'>]> => Join<string, 'property1'> & Join<number, 'property2'>
*/
function CreateClass<N extends number, T{N}, S{N} extends keyof any>(
...properties: [PropertyConfiguration<T{N}, S{N}>]
): new () => Union<[Join<T{N}, S{N}]> {
class NewClass {}
// Here was the logic of executing property constructors
return NewClass;
}
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.