typedots
Version:
A simple way to get and set object properties using paths (aka dot notation) with TypeScript support
67 lines (56 loc) • 4.38 kB
TypeScript
type PathParams<T, Rest> = {
first: T;
rest: Rest;
};
type AnyObject = Record<string, unknown>;
type AcceptNullable<T> = Exclude<T, null | undefined>;
type SanitizeKey<Key, DoSanitize extends boolean> = DoSanitize extends true ? Key extends `${string}.${string}` ? `(${Key})` : Key & string : Key;
type Join<Prefix extends string = '', Key extends string = '', DoSanitize extends boolean = true> = `${Prefix}${SanitizeKey<Key, DoSanitize>}`;
type Matcher<Type, KeyPath extends string, ExpectedType, PreventDistribution extends boolean> = PreventDistribution extends true ? [Type] extends [ExpectedType] ? KeyPath : never : Type extends ExpectedType ? KeyPath : never;
/**
* @example ```ts
* type Input = { one: { subone: { subonetwo: string; } } };
*
* type OutputOne = AddProp<Input, 'one.subone.subonethree', number>;
* declare const outputOne: OutputOne;
* outputOne.one.subone.subonethree = 5;
*
* type OutputTwo = AddProp<Input, 'two', string>;
* declare const outputTwo: OutputTwo;
* outputTwo.two = 'str';
* ```
*/
type AddProp<T extends AnyObject, Path extends string, TargetType, Params extends PathParams<any, any> = Split<Path>> = T & Record<Params['first'], Params['rest'] extends undefined ? TargetType : T[Params['first']] extends AnyObject ? Params['rest'] extends `(${infer A})` ? T[Params['first']] & Record<A, TargetType> : AddProp<T[Params['first']], Params['rest'], TargetType> : Record<Params['rest'], TargetType>>;
type Split<T extends string> = (T extends `(${infer A}).${infer Rest}` ? PathParams<A, Rest> : T extends `${infer A}.${infer Rest}` ? PathParams<A, Rest> : PathParams<T, undefined>);
type ObjectPaths<T, ExpectedType, PreventDistribution extends boolean, Prefix extends string = ""> = T extends never ? never : {
[K in keyof T & string]: AcceptNullable<T[K]> extends AnyObject ? Matcher<T[K], Join<Prefix, K>, ExpectedType, PreventDistribution> | ObjectPaths<AcceptNullable<T[K]>, ExpectedType, PreventDistribution, `${Join<Prefix, K>}.`> : Matcher<T[K], Join<Prefix, K>, ExpectedType, PreventDistribution>;
}[keyof T & string];
type ExtractObjectPaths<T, ExpectedType = DefaultTypedotsParams['expectedType'], PreventDistribution extends boolean = DefaultTypedotsParams['preventDistribution']> = ObjectPaths<T, ExpectedType, PreventDistribution>;
type GetMethod<P extends TypedotsParams = DefaultTypedotsParams> = <BaseObject extends AnyObject, Path extends ExtractObjectPaths<BaseObject, P['expectedType'], P['preventDistribution']>>(object: BaseObject, path: Path) => undefined | any;
type UpdateApplied = boolean;
type HasMethod<P extends TypedotsParams = DefaultTypedotsParams> = <BaseObject extends AnyObject, Path extends ExtractObjectPaths<BaseObject, P['expectedType'], P['preventDistribution']>>(object: BaseObject, path: Path) => boolean;
declare const get: GetMethod;
declare const set: <BaseObject extends AnyObject, Path extends Force extends true ? ExtractObjectPaths<BaseObject, any, true> | string : ExtractObjectPaths<BaseObject, any, true>, Value, Force extends boolean = false>(object: BaseObject, path: Path, value: Value, force?: Force) => object is Force extends true ? Prettify<AddProp<BaseObject, Path, typeof value>> : BaseObject;
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
declare const has: HasMethod;
interface TypedotsParams {
expectedType?: any;
preventDistribution: boolean;
}
type DefaultTypedotsParams = TypedotsParams & {
expectedType: any;
preventDistribution: false;
};
declare class BaseTypedots<P extends TypedotsParams> {
get: GetMethod<P>;
set: typeof set;
has: HasMethod<P>;
}
declare class Typedots<P extends TypedotsParams> implements BaseTypedots<P> {
get: GetMethod<P>;
set: <BaseObject extends AnyObject, Path extends Force extends true ? ExtractObjectPaths<BaseObject, any, true> | string : ExtractObjectPaths<BaseObject, any, true>, Value, Force extends boolean = false>(object: BaseObject, path: Path, value: Value, force?: Force) => object is Force extends true ? AddProp<BaseObject, Path, Value> extends infer T ? { [K in keyof T]: AddProp<BaseObject, Path, Value>[K]; } : never : BaseObject;
has: HasMethod<P>;
}
export { BaseTypedots, type DefaultTypedotsParams, type ExtractObjectPaths, type GetMethod, type HasMethod, type TypedotsParams, type UpdateApplied, Typedots as default, get, has, set };