Improved typing of Creature Properties
This commit is contained in:
@@ -5,23 +5,28 @@ import type {
|
||||
import type {
|
||||
InlineCalculationFieldToCompute, ComputedOnlyInlineCalculationField
|
||||
} from '/imports/api/properties/subSchemas/inlineCalculationField';
|
||||
import type { Simplify } from 'type-fest';
|
||||
|
||||
// It DOES NOT support a constructor with multiple schemas.
|
||||
export type Definition = Exclude<SimpleSchemaDefinition, any[]>;
|
||||
|
||||
// This is a no-op wrapper, effectively implementing a phantom type.
|
||||
export class TypedSimpleSchema<T extends Definition> extends SimpleSchema {
|
||||
constructor(definition: T) {
|
||||
export class TypedSimpleSchema<T> extends SimpleSchema {
|
||||
private constructor(definition: Definition) {
|
||||
super(definition);
|
||||
}
|
||||
static from<D extends Definition>(definition: D): TypedSimpleSchema<InferSchema<D>> {
|
||||
return new TypedSimpleSchema(definition);
|
||||
}
|
||||
// Extending the schema with another schema &'s their definitions
|
||||
extend<U extends Definition>(otherSchema: TypedSimpleSchema<U>): TypedSimpleSchema<T & U> {
|
||||
// In some cases, this is not strictly accurate, use with caution
|
||||
extend<U>(otherSchema: TypedSimpleSchema<U>): TypedSimpleSchema<Simplify<T & U>> {
|
||||
return super.extend(otherSchema);
|
||||
}
|
||||
}
|
||||
|
||||
// It cannot be a method due to https://github.com/microsoft/TypeScript/issues/36931.
|
||||
export function validate<T extends Definition>(schema: TypedSimpleSchema<T>, value: unknown): asserts value is InferSchema<T> {
|
||||
export function validate<T extends Definition>(schema: TypedSimpleSchema<T>, value: unknown): asserts value is T {
|
||||
schema.validate(value);
|
||||
}
|
||||
|
||||
@@ -33,7 +38,7 @@ type NotImplementedMarker = { readonly NotImplementedMarker: unique symbol };
|
||||
type ArrayMarker = { readonly ArrayMarker: unique symbol };
|
||||
type ObjectMarker = { readonly ObjectMarker: unique symbol };
|
||||
|
||||
export type InferType<T> = Expand<MakeUndefinedOptional<InferTypeInner<T>>>;
|
||||
export type InferType<T> = T extends TypedSimpleSchema<infer X> ? X : never;
|
||||
|
||||
// Infer TypeScript type from SimpleSchema type.
|
||||
type InferTypeInner<T> =
|
||||
@@ -47,7 +52,7 @@ type InferTypeInner<T> =
|
||||
T extends typeof String ? string :
|
||||
T extends typeof Date ? Date :
|
||||
T extends RegExp ? string :
|
||||
T extends TypedSimpleSchema<infer U> ? InferSchema<U> :
|
||||
T extends TypedSimpleSchema<infer U> ? U :
|
||||
NotImplementedMarker;
|
||||
|
||||
// Infer TypeScript type from a single field definition.
|
||||
@@ -57,7 +62,7 @@ export type InferField<Def extends Definition, Key extends keyof Def> =
|
||||
? ArrayMarker extends InferTypeInner<Typ>
|
||||
? Array<InferField<Def, `${Key}.$`>>
|
||||
: ObjectMarker extends InferTypeInner<Typ>
|
||||
? { [L in keyof Def as L extends `${Key}.${infer SubKey}` ? SubKey extends `${string}.${string}` ? never : SubKey : never]: InferField<Def, L> }
|
||||
? { [L in keyof Def as L extends `${Key}.${infer SubKey}` ? SubKey extends `${string}.${string}` ? never : SubKey : never]: InferOptional<Def, L, InferField<Def, L>> }
|
||||
: Def[Key] extends { allowedValues: infer Allowed extends string[] } ? InferOptional<Def, Key, InferEnum<Allowed>>
|
||||
: Def[Key] extends { type: 'fieldToCompute' } ? FieldToCalculate
|
||||
: Def[Key] extends { type: 'computedOnlyField' } ? CalculatedOnlyField
|
||||
@@ -73,18 +78,16 @@ type InferEnum<T extends string[]> = T[number];
|
||||
// Infer optional from optional field
|
||||
type InferOptional<Def, Key extends keyof Def, U> = Def[Key] extends { optional: true } ? U | undefined : U;
|
||||
|
||||
type MakeUndefinedOptional<Type> = { [Property in keyof Type as undefined extends Type[Property] ? never : Property]: Type[Property]; }
|
||||
& { [Property in keyof Type as undefined extends Type[Property] ? Property : never]+?: Type[Property]; };
|
||||
type MakeUndefinedOptional<Type> = Simplify<{
|
||||
[Property in keyof Type as undefined extends Type[Property] ? never : Property]: Type[Property];
|
||||
} & {
|
||||
[Property in keyof Type as undefined extends Type[Property] ? Property : never]?: Type[Property];
|
||||
}>;
|
||||
|
||||
// Infer TypeScript type from a schema definition.
|
||||
export type InferSchema<Def extends Definition> = InferField<
|
||||
export type InferSchema<Def extends Definition> = MakeUndefinedOptional<InferField<
|
||||
{ '': { type: typeof Object } }
|
||||
& { [Key in keyof Def as Key extends string ? `.${Key}` : never]: Def[Key] }, ''
|
||||
>;
|
||||
>>;
|
||||
|
||||
// expands object types recursively
|
||||
export type ExpandRecursively<T> = T extends object
|
||||
? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
|
||||
: T;
|
||||
|
||||
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
export type ConvertToUnion<T> = T[keyof T];
|
||||
|
||||
Reference in New Issue
Block a user