|
/** |
|
* NOTE: Types added to this Gist should avoid referencing each other |
|
* where ever possible. This is to make it so types from this Gist can |
|
* be easily copied individually into other projects without having to |
|
* copy the entire Gist. |
|
*/ |
|
|
|
// ## LOW-LEVEL VALUE TYPES |
|
/* These are static types that take no |
|
* no arguments and represent |
|
* primitives */ |
|
|
|
export type PrimitiveValue = string | number | boolean | null; |
|
export type PrintableValue = PrimitiveValue | undefined; |
|
|
|
/** |
|
* When using `keyof` on union types that do not share any |
|
* fields, `keyof` returns `never`. This type is a workaround |
|
* for that behaviour, and will return the union of all the |
|
* keys of the passed type arguments. |
|
*/ |
|
export type AnyKeyOfUnion<T> = T extends T ? keyof T : never; |
|
|
|
// All the possible values that can be returned |
|
// by the `typeof` keyword |
|
export type TypeofReturn = |
|
| "string" |
|
| "number" |
|
| "bigint" |
|
| "boolean" |
|
| "symbol" |
|
| "undefined" |
|
| "object" |
|
| "function"; |
|
|
|
export type Falsy = false | 0 | "" | 0n | null | undefined; |
|
// `NaN` is also falsy, however typescript has |
|
// special behaviour for `NaN` that does not |
|
// allow it to be references as a type literal |
|
|
|
// ## OBJECT TYPES |
|
export type AnyObject = Record<string, unknown> | unknown[]; |
|
export type EmptyObject = Record<never, never> | []; |
|
|
|
// ## VALUE COMPOSITION TYPES |
|
/* These types take type arguments and use |
|
them to compose more complex types */ |
|
|
|
export type Dictionary<T> = Record<string, T>; |
|
|
|
export type NonEmptyArray<T> = [T, ...T[]]; |
|
/* This is not actually particularly |
|
useful. The idea behind this was to |
|
use it as the type of a function's |
|
parameter, but in an actual codebase, |
|
arrays that we know aren't empty |
|
will probably still be typed with the |
|
standard `[]` typing, so passing it to |
|
a function that expects `NonEmptyArray` |
|
will trigger a typescript warning */ |
|
|
|
/** |
|
* Widens typings for literals and tuples to their |
|
* primitive types |
|
*/ |
|
type Widen<T> = T extends string |
|
? string |
|
: T extends number |
|
? number |
|
: T extends boolean |
|
? boolean |
|
: T extends any[] |
|
? Widen<T[number]>[] |
|
: T; |
|
|
|
// A value stored in a passed array or object |
|
// type |
|
export type ValueFrom<T> = T extends unknown[] | readonly unknown[] |
|
? T[number] |
|
: T[keyof T]; |
|
|
|
export type ObjectKeyValuePairs<Dict extends Record<string, unknown>> = |
|
ValueFrom<{ |
|
[Key in keyof Dict]: [Key, Dict[Key]]; |
|
// This does look a bit weird, but doing it this way means that |
|
// typescript can infer the type of the value item from the key |
|
// Eg; if an object has a `number` at the `"value"` key, then |
|
// if you do `pair[0] === "key"` typescript will then infer that |
|
// `pair[1]` is a `number` |
|
}>; |
|
|
|
export type DeepPartial<TheObject extends Record<string, unknown>> = { |
|
[Key in keyof TheObject]?: TheObject[Key] extends Record<string, unknown> |
|
? DeepPartial<TheObject[Key]> |
|
: TheObject[Key]; |
|
}; |
|
|
|
// Alternate version of `Extract` that enforces overlap between |
|
// the type arguments |
|
export type StrictExtract<Base, Sub extends Base> = Extract<Base, Sub>; |
|
|
|
// Version of `Omit` where the keys being omitted |
|
// must actually be keys of the base type. This |
|
// should be used instead of `Omit` unless you are |
|
// dealing with dynamic types and can't be sure |
|
// that that rule will be followed. |
|
export type StrictOmit<BaseType, ToOmit extends keyof BaseType> = Omit< |
|
BaseType, |
|
ToOmit |
|
>; |
|
|
|
// Take an object type and make specific fields |
|
// non-optional |
|
export type RequireSpecificKeys< |
|
TheObject extends Record<string, unknown>, |
|
Keys extends keyof TheObject, |
|
> = TheObject & Required<Pick<TheObject, Keys>>; |
|
|
|
// Take an object type and make specific fields |
|
// optional |
|
export type PartialSpecificKeys< |
|
TheObject extends Record<string, unknown>, |
|
Keys extends keyof TheObject, |
|
> = Omit<TheObject, Keys> & Partial<Pick<TheObject, Keys>>; |
|
|
|
// Removes the `readonly` property from a type |
|
export type Mutable<Obj extends AnyObject> = { |
|
-readonly [P in keyof Obj]: Obj[P]; |
|
}; |
|
|
|
export type ShallowMerge< |
|
Source extends Record<string, any>, |
|
Update extends Record<string, any>, |
|
> = Omit<Source, keyof Update> & Update; |
|
|
|
// Replace the type of a specific field(s) |
|
// in an object type |
|
export type ReplaceFieldType< |
|
Dict extends Record<string, any>, |
|
Key extends keyof Dict, |
|
Replacement, |
|
> = Omit<Dict, Key> & Record<Key, Replacement>; |
|
|
|
// Get keys of the base type that are of a specific type |
|
export type ExtractKeyOf<BaseType, KeyExtraction> = Extract< |
|
keyof BaseType, |
|
KeyExtraction |
|
>; |
|
export type StringKeyOf<BaseType> = ExtractKeyOf<BaseType, string>; |
|
|
|
export type ExcludeKeyOf<BaseType, KeyExclusion> = Exclude< |
|
keyof BaseType, |
|
KeyExclusion |
|
>; |
|
|
|
export type PrefixKeys< |
|
Dict extends Record<string, unknown>, |
|
Prefix extends string, |
|
> = { |
|
[Key in StringKeyOf<Dict> as `${Prefix}${Key}`]: Dict[Key]; |
|
}; |
|
|
|
export type ConditionalUnion< |
|
Base, |
|
Incoming, |
|
Condition extends boolean, |
|
> = Condition extends true ? Base | Incoming : Base; |
|
|
|
// Invert a boolean type |
|
export type Not<Bool extends boolean> = Bool extends true ? false : true; |
|
|
|
export type DeepOmitOptional<Obj extends Record<any, unknown>> = { |
|
[Key in keyof Obj as Obj[Key] extends Required<Obj>[Key] |
|
? Key |
|
: never]: Obj[Key] extends Record<any, unknown> |
|
? DeepOmitOptional<Obj[Key]> |
|
: Obj[Key]; |
|
}; |
|
|
|
/** |
|
* Pick some keys from an object type and make those |
|
* fields required, except for some exceptions which |
|
* will simply be picked without being marked as |
|
* required |
|
*/ |
|
export type PickAndMakeRequiredWithExceptions< |
|
Base, |
|
KeysToPick extends keyof Base, |
|
KeysToNotMakeRequired extends KeysToPick, |
|
> = Pick<Base, KeysToNotMakeRequired> & |
|
Required<Omit<Pick<Base, KeysToPick>, KeysToNotMakeRequired>>; |
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation> |
|
export type KeyOfUnion<T> = T extends any ? keyof T : never; |
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation> |
|
export type GetFieldValueFromUnion<T, K extends keyof T> = T extends any |
|
? K extends keyof T |
|
? T[K] |
|
: never |
|
: never; |
|
|
|
/** |
|
* Performs a 1-layer deep object merge where the types are listed |
|
* as unions rather than being passed as generic type arguments. |
|
* Useful for merging a dynamic number of object types. |
|
*/ |
|
export type MergeUnion<T> = { |
|
[K in KeyOfUnion<T>]: GetFieldValueFromUnion<T, K>; |
|
}; |
|
|
|
/** |
|
* An object that must have at least one field from the |
|
* passed object type. Can have multiple fields. |
|
* |
|
* USE CASE: You have a component that has some dynamic |
|
* sizing logic, but requires that either width or height |
|
* be specified, but also allows for both to be specified. |
|
* `AtLeastOneField<{ width: number, height: number }>` |
|
*/ |
|
export type AtLeastOneField<Obj extends Record<string, unknown>> = ValueFrom<{ |
|
[K in keyof Obj]: Pick<Obj, K> & Partial<Omit<Obj, K>>; |
|
}>; |
|
|
|
/** |
|
* Must match an object type exactly, or be an empty object. |
|
* |
|
* USE CASE: You may have an instance where an API has some |
|
* optional params, but some are grouped, so if one param is |
|
* provided then others must be as well. Eg; in the BTC bridge |
|
* API, if you make use of the optional `parentEnity` param, |
|
* then you must must also provide the `peid` param, but you |
|
* can also provide neither, so you could use this type to |
|
* make sure that if either `parentEntity` or `peid` is |
|
* provided, then both must be provided. |
|
*/ |
|
export type AllOrNone<T> = T | { [K in keyof T]?: never }; |
|
|
|
// ## FUNCTION TYPES |
|
|
|
// Can be either a static value, or a getter |
|
// function for deriving a value |
|
export type GetterOrStatic<ReturnType, Params extends any[] = []> = |
|
| ((...params: Params) => ReturnType) |
|
| ReturnType; |
|
|
|
export type Comparator<T> = (a: T, b: T) => boolean; |
|
export type SortComparator<T> = (a: T, b: T) => number; |
|
|
|
// ## MISC TYPES |
|
|
|
// When you want to specify auto-complete suggestions in |
|
// the editor while also allowing values outside of the |
|
// suggestions to be provided. |
|
// NOTE: Trust me, the use of `Omit` is not a mistake, for |
|
// whatever reasons you have to use it instead of `Exclude` |
|
// for this to work with strings. Not sure about other types |
|
// as I have only tested with strings. |
|
type WithLooseAutocomplete<AcceptedType, Suggestions extends AcceptedType> = |
|
| Suggestions |
|
| Exclude<Omit<AcceptedType, Suggestions & keyof AcceptedType>, Suggestions>; |