obsidian-dev-utils
Version:
This is the collection of useful functions that you can use for your Obsidian plugin development
123 lines (122 loc) • 6.22 kB
text/typescript
/**
* @packageDocumentation
*
* Type utilities.
*/
/**
* A type that represents the keys of an object as strings and asserts that all keys are present in a list of keys.
*
* @typeParam Type - The type of the object.
* @typeParam Keys - The list of keys to assert.
*/
export type ExactKeys<Type extends object, Keys extends readonly string[]> = ExactMembers<StringKeys<Type>, Keys>;
/**
* A type that represents a return value that may be `void`.
*
* @typeParam T - The type of the value that may be returned.
*/
export type MaybeReturn<T> = T | void;
/**
* A type that represents the values of an object.
*
* @typeParam T - The type of the object.
*/
export type PropertyValues<T extends object> = T[StringKeys<T>];
/**
* A type that represents the keys of an object as strings.
*
* @typeParam T - The type of the object.
*/
export type StringKeys<T extends object> = Extract<keyof T, string>;
type LastInUnion<Union> = UnionToIntersection<Union extends unknown ? () => Union : never> extends () => infer Last ? Last : never;
type UnionToIntersection<Union> = (Union extends unknown ? (key: Union) => void : never) extends (key: infer Intersection) => void ? Intersection : never;
type UnionToTuple<Union, Last = LastInUnion<Union>> = [Union] extends [never] ? [] : [...UnionToTuple<Exclude<Union, Last>>, Last];
/**
* A type that represents the members of a type.
*
* @typeParam Type - The type to assert the members of.
* @typeParam Keys - The list of members to assert.
*/
export type ExactMembers<Type extends LiteralKey, Keys extends readonly LiteralKey[]> = Exclude<Keys[number], Type> extends never ? Exclude<Type, Keys[number]> extends never ? Duplicates<Keys> extends [] ? Keys : `ERROR: Duplicate members: ${TupleToCSV<Duplicates<Keys>>}` : `ERROR: Missing members: ${TupleToCSV<UnionToTuple<Exclude<Type, Keys[number]>>>}` : `ERROR: Invalid members: ${TupleToCSV<UnionToTuple<Exclude<Keys[number], Type>>>}`;
type Duplicates<T extends readonly unknown[], Seen extends readonly unknown[] = [], Added extends readonly unknown[] = [], Out extends readonly unknown[] = []> = T extends readonly [infer First, ...infer Rest] ? Includes<Seen, First> extends true ? Includes<Added, First> extends true ? Duplicates<Rest, Seen, Added, Out> : Duplicates<Rest, Seen, [...Added, First], [...Out, First]> : Duplicates<Rest, [...Seen, First], Added, Out> : Out;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type Includes<Type extends readonly unknown[], Member> = Type extends readonly [infer First, ...infer Rest] ? Equal<First, Member> extends true ? true : Includes<Rest, Member> : false;
type LiteralKey = number | string;
type ToString<T> = T extends number | string ? `${T}` : never;
type TupleToCSV<Tuple extends readonly unknown[]> = Tuple extends readonly [infer First, ...infer Rest] ? First extends LiteralKey ? Rest extends readonly unknown[] ? Rest['length'] extends 0 ? ToString<First> : `${ToString<First>},${TupleToCSV<Rest>}` : never : never : '';
/**
* Asserts that all keys of a type are present in a list of keys.
*
* @typeParam Type - The type to assert the keys of.
* @typeParam Keys - The list of keys to assert.
* @param _type - The type to assert the keys of.
* @param keys - The list of keys to assert.
* @returns The list of keys.
*
* @remarks If the incorrect keys are provided, the function has a compile-time error.
*
* @example
* ```ts
* type A = { a: 1, b: 2, c: 3 };
* assertAllTypeKeys<A>(typeToDummyParam<A>(), ['a', 'b', 'c']); // OK
* assertAllTypeKeys<A>(typeToDummyParam<A>(), ['c', 'a', 'b']); // OK, order is ignored
* assertAllTypeKeys<A>(typeToDummyParam<A>(), ['a', 'b', 'c', 'd']); // Error: Invalid members: d
* assertAllTypeKeys<A>(typeToDummyParam<A>(), ['a', 'b']); // Error: Missing members: c
* assertAllTypeKeys<A>(typeToDummyParam<A>(), ['a', 'a', 'b', 'c', 'c']); // Error: Duplicate members: a,c
* ```
*/
export declare function assertAllTypeKeys<Type extends object, const Keys extends readonly string[]>(_type: Type, keys: ExactMembers<StringKeys<Type>, Keys>): readonly (keyof Type)[];
/**
* Asserts that all members of a union are present in a list of members.
*
* @typeParam Type - The type to assert the members of.
* @typeParam Keys - The list of members to assert.
* @param _type - The type to assert the members of.
* @param keys - The list of members to assert.
* @returns The list of members.
*
* @remarks If the incorrect members are provided, the function has a compile-time error.
*
* @example
* ```ts
* type A = 1 | 2 | 3 | 'a';
*
* assertAllUnionMembers(typeToDummyParam<A>(), [1, 2, 3, 'a']); // OK
* assertAllUnionMembers(typeToDummyParam<A>(), [3, 2, 1, 'a']); // OK, order is ignored
* assertAllUnionMembers(typeToDummyParam<A>(), [1, 2, 3, 'a', 4]); // Error: Invalid members: 4
* assertAllUnionMembers(typeToDummyParam<A>(), [1, 2, 3,]); // Error: Missing members: a
* assertAllUnionMembers(typeToDummyParam<A>(), [1, 2, 3, 'a', 'a']); // Error: Duplicate members: 1,a
* ```
*/
export declare function assertAllUnionMembers<const Type extends LiteralKey, const Keys extends readonly LiteralKey[]>(_type: Type, keys: ExactMembers<Type, Keys>): readonly Type[];
/**
* Converts a type to a dummy parameter.
*
* This helper function is useful when we need to get type inference when we cannot use generic type parameters.
*
* An example below shows such a scenario.
*
* @typeParam T - The type to convert.
* @returns A dummy parameter of the type.
*
* @remarks The result should be used only for type inference. The value should not be used directly.
*
* @example
* ```ts
* type A = { c: number; };
* type B = { d: string; }
*
* function g<T, U>(u: U) {}
*
* // We cannot have partial type inference.
* g<A>({ d: 'foo' }); // Error: Expected 2 type arguments, but got 1. ts(2558)
*
* // We have to call instead
* g<A, B>({ d: 'foo' }); // OK, but we could not use type inference for `U=B`.
*
* function g2<T, U>(_type: T, u: U) {}
* g2(typeToDummyParam<A>(), { d: 'foo' }); // We could use type inference for `T=A` and `U=B`.
* ```
*/
export declare function typeToDummyParam<T>(): T;
export {};