-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from Pduhard/master
feat: improve type definition for Abitbol classes
- Loading branch information
Showing
1 changed file
with
154 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,168 @@ | ||
export = Class | ||
// #region Constructor Arguments | ||
|
||
type PropName<T extends string> = Uncapitalize<T> | ||
type InitArgType<E, I> = E extends { __init__: (...args: infer A) => any } | ||
? A | ||
: I extends { __init__: (...args: infer A) => any } | ||
? A | ||
: any[] | ||
|
||
type GetterPropNameFromGet<T extends string> = T extends `get${infer Prefix}` ? PropName<Prefix> : never; | ||
type GetterPropNameFromSet<T extends string> = T extends `is${infer Prefix}` ? PropName<Prefix> : never; | ||
type GetterPropName<T extends string> = GetterPropNameFromGet<T> | GetterPropNameFromSet<T> | ||
type GetterValue<T> = T extends (...args: any[]) => infer R ? R : any; | ||
// #endregion | ||
|
||
type ObjectDescriptor<Type> = { | ||
[Property in keyof Type]: Type[Property]; | ||
} | ||
// #region Computed Properties | ||
|
||
type StringKey<Type> = keyof Type extends string ? keyof Type : never; | ||
type GetterIdentifier<K> = K extends `get${infer R}` | `is${infer R}` | `has${infer R}` ? Uncapitalize<R> : never; | ||
type SetterIdentifier<K> = K extends `set${infer R}` ? Uncapitalize<R> : never; | ||
type GetterOrSetterIdentifier<K> = K extends `get${infer R}` | `is${infer R}` | `has${infer R}` | `set${infer R}` ? Uncapitalize<R> : never; | ||
type ReadonlyGetterIdentifier<I, SI> = Pick<SI, Exclude<keyof SI, keyof I>> extends string ? never : I; | ||
|
||
type ComputedProps<Type> = { | ||
[Property in StringKey<Type> as GetterPropName<Property>]: GetterValue<Type[Property]> | ||
} | ||
type GetterValue<V> = V extends () => infer R ? R : never; | ||
type SetterValue<V> = V extends (value: infer R) => void ? R : never; | ||
|
||
type AbitbolComputedProps<Type> = ObjectDescriptor<Type> & ComputedProps<Type> | ||
type ClassDescriptor = { | ||
[x: string]: any, | ||
__include__: any[], | ||
__classvars__: any, | ||
__init__(...args: any[]): void, | ||
__preBuild__(properties, NewClass, SuperClass): void, | ||
__postBuild__(properties, NewClass, SuperClass): void, | ||
} | ||
type ExtractGetters<T> = { | ||
[K in keyof T as GetterIdentifier<K>]: GetterValue<T[K]>; | ||
}; | ||
|
||
type ExtractSetters<T> = { | ||
[K in keyof T as SetterIdentifier<K>]: SetterValue<T[K]>; | ||
}; | ||
|
||
type ExtractSetterIdentifier<T> = { | ||
[K in keyof T as SetterIdentifier<K>]: K; | ||
}; | ||
|
||
type ExtractReadonlyGetters<T> = { | ||
readonly [K in keyof T as ReadonlyGetterIdentifier<GetterIdentifier<K>, ExtractSetterIdentifier<T>>]: GetterValue<T[K]>; | ||
}; | ||
|
||
type WritableOrReadonly<A, R> = R & Pick<A, Exclude<keyof A, keyof R>>; | ||
type Getter<P> = WritableOrReadonly<ExtractGetters<P>, ExtractReadonlyGetters<P>>; | ||
type Setter<P> = ExtractSetters<P>; | ||
type GetterAndSetter<P> = Getter<P> & Setter<P>; | ||
|
||
type GetGetterMethodName<K extends string, P> = `get${Capitalize<K>}` extends keyof P | ||
? `get${Capitalize<K>}` : `is${Capitalize<K>}`extends keyof P | ||
? `is${Capitalize<K>}` : `has${Capitalize<K>}`extends keyof P | ||
? `has${Capitalize<K>}` : undefined; | ||
|
||
type GetSetterMethodName<K extends string, P> = `set${Capitalize<K>}`extends keyof P | ||
? `set${Capitalize<K>}` : undefined; | ||
|
||
type IsGetterOrSetterIdentifier<K> = K extends `get${string}` | `is${string}` | `has${string}` | `set${string}` ? K : never; | ||
|
||
type MayBeSetter<T> = T extends undefined ? {} : { set: T }; | ||
type MayBeGetter<T> = T extends undefined ? {} : { get: T }; | ||
|
||
// #endregion | ||
|
||
// #region Mixin | ||
|
||
type UnionToIntersection<U> = | ||
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) | ||
? I | ||
: never; | ||
|
||
type UnionOfArrayElements<T> = T extends (infer U)[] ? U : never; | ||
|
||
type ExtractUnion<T> = T extends { __include__: infer U } ? UnionOfArrayElements<U> : never; | ||
|
||
type ExtractMixins<E, I extends { prototype: any }> = UnionToIntersection<ExtractUnion<E & I["prototype"]>> | ||
|
||
type ExtractMixinsStaticProperties<T> = T extends { __include__: infer U } | ||
? UnionToIntersection<ExtractStaticProperties<UnionOfArrayElements<U>>> | ||
: NonNullable<unknown> | ||
|
||
// #endregion | ||
|
||
declare interface AbitbolClass<T> { | ||
new(params?: any): AbitbolObject<T>, | ||
// #region Static Properties | ||
|
||
type ExtractStaticProperties<T> = (T extends { __classvars__: infer U } ? U : NonNullable<unknown>) | ||
& ExtractMixinsStaticProperties<T>; | ||
|
||
// #endregion | ||
|
||
// #region Special Properties | ||
|
||
type ExtractAttributesPropertiesKeys<P> = { | ||
[K in keyof P]: P[K] extends (...args: any[]) => any ? never : K; | ||
}[keyof P] | ||
|
||
type ExtractMethodsPropertiesKeys<P> = { | ||
[K in keyof P]: P[K] extends (...args: any[]) => any ? K : never; | ||
}[keyof P] | ||
|
||
type AttributesPropertiesKeys<P> = ExtractAttributesPropertiesKeys<P>; | ||
|
||
type MethodsPropertiesKeys<P> = ExtractMethodsPropertiesKeys<P>; | ||
|
||
type SuperVariables<P> = { | ||
$class: any, | ||
$data: Record<string, any>, | ||
$map: { | ||
attributes: {} | ||
methods: {} | ||
computedProperties: {} | ||
attributes: { | ||
[K in AttributesPropertiesKeys<P>]: true; | ||
}, | ||
computedProperties: { | ||
[K in keyof GetterAndSetter<P> & string]: | ||
& MayBeGetter<GetGetterMethodName<K, P>> | ||
& MayBeSetter<GetSetterMethodName<K, P>> | ||
& { annotations: Record<string, any> } | ||
}, | ||
methods: { | ||
[K in MethodsPropertiesKeys<P>]: { | ||
annotations: Record<string, any>, | ||
} | ||
}, | ||
} | ||
$extend<ClassDescriptor>(properties: ClassDescriptor): AbitbolClass<ClassDescriptor & T> | ||
} | ||
}; | ||
|
||
declare type AbitbolObject<Type> = AbitbolComputedProps<Type> & { | ||
$class: AbitbolClass<Type>, | ||
$map: { | ||
attributes: {}, | ||
methods: {}, | ||
computedProperties: {}, | ||
}, | ||
$data: any, | ||
$super(...args): any, | ||
$name: string, | ||
$computedPropertyName: string, | ||
type WithSpecialProperties<E, I extends { prototype: any }, K extends keyof E, PI> = PI | ||
& (I["prototype"][K] extends CallableFunction ? { $super: I["prototype"][K] } : NonNullable<unknown>) | ||
& (K extends IsGetterOrSetterIdentifier<K> ? { $computedPropertyName: GetterOrSetterIdentifier<K> } : NonNullable<unknown>) | ||
& { $name: K } | ||
|
||
type InjectSpecialProperties<E, I extends { prototype: any }, PI> = { | ||
[K in keyof E]: E[K] extends (...args: infer A) => infer R | ||
? (this: WithSpecialProperties<E, I, K, PI>, ...args: A) => R | ||
: E[K] | ||
} | ||
|
||
declare namespace Class { | ||
function $class(): void | ||
namespace $class { | ||
namespace $map { | ||
const attributes: {} | ||
const methods: {} | ||
const computedProperties: {} | ||
} | ||
// #endregion | ||
|
||
type ExtractProperties<E, I extends { prototype: any }, SP> = Omit< | ||
Omit<I["prototype"], keyof E | keyof SP> & E & ExtractMixins<E, I>, | ||
"__include__" | "__classvars__" | ||
> | ||
|
||
type PublicInstance<P, S> = GetterAndSetter<P> & S & P | ||
|
||
type PrivateInstance<E, I extends { prototype: any }, PI> = InjectSpecialProperties<E, I, PI>; | ||
|
||
export type ExtendedAbitbolClass<E, I, SP, P, S, PI> = Omit<I, keyof E> | ||
& { prototype: P } | ||
& S | ||
& SP | ||
& (new (...args: InitArgType<E, I>) => PI) | ||
|
||
export class AbitbolClass { | ||
static $extend< | ||
E extends object, // Extended properties | ||
I extends { prototype: any }, // Super class | ||
SP = ExtractStaticProperties<E & I["prototype"]>, // Static properties | ||
P = ExtractProperties<E, I, SP>, // properties | ||
S = SuperVariables<P>, // Super variables | ||
PI = PublicInstance<P, S> // Public instance | ||
>( | ||
this: I, | ||
properties?: PrivateInstance<E, I, PI> | ||
): ExtendedAbitbolClass<E, I, SP, P, S, PI> | ||
|
||
static $class: any; | ||
|
||
static $map: { | ||
attributes: { }, | ||
computedProperties: { }, | ||
methods: { }, | ||
} | ||
} | ||
|
||
function $extend<T>(properties: T): AbitbolClass<T> | ||
} | ||
export default AbitbolClass; |