Skip to content

Instantly share code, notes, and snippets.

@pshaddel
Created October 23, 2023 07:54
Show Gist options
  • Save pshaddel/d10da39b2409fd9426a93d092bc66522 to your computer and use it in GitHub Desktop.
Save pshaddel/d10da39b2409fd9426a93d092bc66522 to your computer and use it in GitHub Desktop.
// Step -2 - Bugs
// Step -1 - Dirty Way
// Step 0 - Projection should only accepts fields from interface
// Step 1 - Projection should be typed and only accepts mongodb valid values[0, 1, true, false]
// Step 2 - Projection should be optional
// Step 3 - Projection should support nested projection
// Step 4 - Projection should support nested projection with arrays(i)
// Step 5 - Projection should be nested optional
// Step 6 - MongoDB Projection rule, inclusion and exclusion cannot be mixed
// - Function output should match the projection:
// Step 7 - only normal fiels - only inclusion
// Step 8 - objects - only inclusion
// Step 9 - objects and arrays - only inclusion
// Step 10 - normal fields exclusion
import mongoose from 'mongoose';
const mongooseSchema = new mongoose.Schema<IUser>({
name: String,
email: String,
addresses: [
{
street: String,
city: String
}
],
accountInfo: {
debit: Number,
credit: Number,
other: {
directDebit: Number
}
}
});
const UserModel = mongoose.model<IUser>('User', mongooseSchema);
function getUser<T extends Projection<IUser>>(email: string, projection: T) {
type OutputTypeInclusion<T, DocType = IUser> = DocType extends Array<infer U>
? OutputTypeInclusion<T, U>
: {
[key in keyof T]: T[key] extends PrimitivValues
? DocType[key extends keyof DocType ? key : never]
: OutputTypeInclusion<T[key], DocType[key extends keyof DocType ? key : never]>;
};
type X = OutputTypeInclusion<T, IUser>;
return UserModel.findOne({ email }, { accountInfo: true }).lean() as unknown as X;
}
type Exclude<T, K> = {
[key in keyof T]: key extends keyof K ? never : T[key];
};
type X = Exclude<{ name: string; age: number }, { name: 1 }>;
const x: X = {};
function getDebitFromUser(user: {
accountInfo: {
debit: number;
};
}) {
return user.accountInfo.debit;
}
const main = async () => {
const user = await getUser('test@gmail.com', { email: 0 });
if (!user) {
return;
}
const debit = getDebitFromUser(user);
console.log(debit, user);
const exclusUser = await getUser('test@gmail.com', { email: 1 });
console.log(exclusUser);
};
main();
interface IUser {
name: number;
email?: string;
tags: string[];
addresses: {
street: string;
city: string;
}[];
accountInfo: {
debit: number;
credit?: number;
other: {
directDebit: number;
};
};
}
type PrimitivValues = string | number | boolean | Date;
type Value = 0 | 1 | true | false;
type TruthyValue = 1 | true;
type FalsyValue = 0 | false;
type Project<T, Value> = {
[key in keyof T]?: T[key] extends PrimitivValues ? Value : Value | GetElement<Project<T[key], Value>>;
};
type Projection<T> = Project<T, TruthyValue> | Project<T, FalsyValue>;
type GetElement<T> = T extends Array<infer U> ? U : T;
type X1 = GetElement<{ name: string }[]>;
// type ReturnType<T> = T extends (...args: any) => infer U ? U : never;
const sum = (a: string, b: string) => a + b;
type Y = ReturnType<typeof sum>;
const pro: Projection<IUser> = {
addresses: 0,
name: false,
accountInfo: {
credit: false
}
};
type ArrayOperators = { $slice: number | [number, number]; $elemMatch?: undefined } | { $elemMatch: object; $slice?: undefined };
type Projector<T, Element> = T extends Array<infer U>
? Projector<U, Element> | ArrayOperators
: T extends object
? {
[K in keyof T]?: T[K] extends object ? Projector<T[K], Element> | Element : Element;
}
: Element;
type _IDType = { _id?: boolean | 1 | 0 };
type InclusionProjection<T> = NestedPartial<Projector<NestedRequired<T>, true | 1> & _IDType>;
type ExclusionProjection<T> = NestedPartial<Projector<NestedRequired<T>, false | 0> & _IDType>;
type NestedRequired<T> = T extends Array<infer U>
? Array<NestedRequired<U>>
: T extends object
? {
[K in keyof T]-?: NestedRequired<T[K]>;
}
: T;
type NestedPartial<T> = T extends object
? {
[K in keyof T]?: NestedPartial<T[K]>;
}
: T;
// https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object/58436959#58436959
type DotPrefix<T extends string> = T extends '' ? '' : `.${T}`;
type DotNestedKeys<T> = (
T extends object ? { [K in Exclude<keyof T, symbol>]: `${K}` | `${K}${DotPrefix<DotNestedKeys<T[K]>>}` }[Exclude<keyof T, symbol>] : ''
) extends infer D
? Extract<D, string>
: never;
type FindDottedPathType<T, Path extends string> = Path extends `${infer K}.${infer R}`
? K extends keyof T
? FindDottedPathType<T[K], R>
: never
: Path extends keyof T
? T[Path]
: never;
type ExtractNestedArrayElement<T> = T extends (infer U)[] ? ExtractNestedArrayElement<U> : T;
type DotKeys<ProjectionType, DocType> = {
[key in DotNestedKeys<ProjectionType>]: FindDottedPathType<DocType, key>;
};
// type GetArrayElement<T> = T extends (infer U)[] ? U : T;
// type ZZ = DotKeys<NestedObjectType, ExtractNestedArrayElement<NestedObjectType>>;
type AnyObject = { [key: string]: any };
export type ProjectionType<T> =
| ((DotKeys<InclusionProjection<T>, T> | DotKeys<ExtractNestedArrayElement<ExclusionProjection<T>>, T>) &
AnyObject)
| string
| ((...agrs: any) => any);
type X = InclusionProjection<{
a: number;
b: {
c: {
d: string;
};
};
}>;
type NestedObjectType = {
a: string | number;
b: string;
nest: {
c: {
d: string;
h: number;
z: { o: number }[];
}[];
};
otherNest: {
c: string;
};
};
type Extracted = ExtractNestedArrayElement<NestedObjectType>;
type Projected = ProjectionType<NestedObjectType>;
type FlattenArraysOfObjectsNested
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment