Skip to content

Instantly share code, notes, and snippets.

@niieani
Created November 26, 2017 12:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niieani/37a872f808c34049523ea0f7252e9019 to your computer and use it in GitHub Desktop.
Save niieani/37a872f808c34049523ea0f7252e9019 to your computer and use it in GitHub Desktop.
TypeScript utils
// many based/copied from https://github.com/tycho01/typical/tree/master/src
// utility types:
type False = "0";
type True = "1";
type Bool = True | False;
type If<Cond extends Bool, Then, Else> = { 1: Then; 0: Else }[Cond];
type WrapInPromiseIfTrue<T, IsAsync extends Bool> = If<
IsAsync,
Promise<T>,
T
>;
type AsyncProperty = "@@isAsync";
type AsyncOrSync = "async" | "sync";
type AsyncTag = {'async'?: true}
type AsyncTagProperty = 'async'
type SyncTag = {'sync'?: true}
// type WrapInPromiseIfAsyncTag<TaggedT, WrappedT = GetSourceIfDefined<TaggedT>> = If<
// UnionHasKey<keyof NonNull<TaggedT>, AsyncTagProperty>,
// Promise<WrappedT>,
// WrappedT
// >
type WrapInPromiseIfAsyncTag<TaggedT, WrappedT = GetSourceIfDefined<TaggedT>> = WrapInPromiseIfTrue<
WrappedT,
UnionHasKey<keyof NonNull<TaggedT>, AsyncTagProperty>
>
type HasSourceTag<T> = UnionHasKey<keyof NonNull<T>, SourceTag>
type WrapInPromiseIfAsyncTagTest = WrapInPromiseIfAsyncTag<{} & SourceReference<{yup: 1}> & AsyncTag>
type MaybeAsyncFromKey<T, Key extends AsyncOrSync> = {
'async': Promise<T>,
'sync': T,
[Else: number]: never,
}[Key];
type MapToBool<T> = AllDefinedAsTrue<T> & AllUndefinedAsFalse & Padding
// declare var mapToBool1 : MapToBool<{}>
type Get<
MappedT,
Property,
> = MappedT[keyof MappedT & Property];
type SourceTag = "source";
type ReferenceTag = "@@reference";
// type ForEachAdd<T, AddT> = RecursivelyExtend<T, AddT>
// type ArrayToModified<
// TMaybeArr,
// // extract T from Array<T>:
// T = TMaybeArr[keyof TMaybeArr & "0"],
// Trans = MapToBool<TMaybeArr>,
// // if has: copyWithin
// X extends Bool = Trans[keyof Trans & 'copyWithin']
// > = If<
// X,
// Array<T & { dupa: 1 }>,
// TMaybeArr
// >
// type ForEachWithArraySupport2<
// TMaybeArr,
// AddT,
// // extract T from Array<T>:
// T = TMaybeArr[keyof TMaybeArr & "0"],
// Trans = MapToBool<TMaybeArr>,
// // if has: copyWithin
// X extends Bool = Trans[keyof Trans & 'copyWithin']
// > = If<
// X,
// ForEachAdd<Array<ForEachWithArraySupport<T, AddT>>, AddT>,
// ForEachAdd<TMaybeArr, AddT>
// >
// type ForEachWithArraySupport<
// TMaybeArr,
// AddT,
// // extract T from Array<T>:
// T = TMaybeArr[keyof TMaybeArr & "0"],
// Trans = MapToBool<TMaybeArr>,
// // if has: copyWithin
// X extends Bool = Trans[keyof Trans & 'copyWithin']
// > = If<
// X,
// ForEachAdd<Array<TAdded>, AddT>,
// ForEachAdd<TMaybeArr, AddT>
// >
type Obj<T> = { [k: string]: T };
type And<
A extends Bool,
B extends Bool
> = ({ 1: { 1: '1' } & Obj<'0'> } & Obj<Obj<'0'>>)[A][B];
type Or<
A extends Bool,
B extends Bool
> = ({ 0: { 0: '0' } & Obj<'1'> } & Obj<Obj<'1'>>)[A][B];
type UnionHasKey<Union extends string, K extends string> = ({[S in Union]: '1' } & Obj<'0'>)[K];
type Indeterminate<T extends string> = And<
UnionHasKey<T, '0'>,
UnionHasKey<T, '1'>
>;
type Not<T extends Bool> = { '1': '0'; '0': '1'; }[T];
type Determinate<T extends Bool> = Not<Indeterminate<T>>;
type DefinitelyYes<T extends Bool> = And<T, Determinate<T>>;
type UnionContained<T extends string, U extends string> = DefinitelyYes<({ [P in U]: '1' } & Obj<'0'>)[T | U]>;
type UnionEmpty<T extends string> = And<UnionContained<T, 'foo'>, UnionContained<T, 'bar'>>;
type UnionToObject<Keys extends string> = { [K in Keys]: K };
type Keyed<T> = {[K in keyof T]: K };
type KeyedSafe<T> = Keyed<T> & Obj<never>;
type IntersectionUnions<Big extends string, Small extends string> = KeyedSafe<UnionToObject<Small>>[Big];
type UnionsOverlap<Big extends string, Small extends string> = Not<UnionEmpty<IntersectionUnions<Big, Small>>>;
// type ATW = UnionsOverlap<'filter', keyof Array<any>>
// type ArrPrototypeMethods = 'length' | 'push' | 'pop' | 'concat' | 'join' | 'reverse' | 'shift' | 'slice' | 'sort' | 'splice' | 'unshift' | 'indexOf' | 'lastIndexOf' | 'every' | 'some' | 'forEach' | 'map' | 'filter' | 'reduce' | 'reduceRight' | 'find' | 'findIndex' | 'fill' | 'copyWithin' | 'entries' | 'keys' | 'values'
// "includes" | "length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight" | "entries" | "keys" | "values" | "find" | "findIndex" | "fill" | "copyWithin"
type DiffUnion<T extends string, U extends string> =
({[P in T]: P } &
{ [P in U]: never } & // toString: "toString"; toLocaleString: "toLocaleString";
{ [k: string]: never })[T]; // toString: "toString"; toLocaleString: "toLocaleString";
type ObjectHasKey<
O extends {},
K extends string
> = UnionHasKey<keyof O, K>;
type ArrPrototypeMethods = DiffUnion<keyof Array<any>, 'toString' | 'toLocaleString'>
type IsArrayType<T> = DefinitelyYes<ObjectHasKey<T, ArrPrototypeMethods>>
type TypeProperties<X> = DiffUnion<keyof X, 'toString' | 'toLocaleString' | 'valueOf'>
// type BoolProperties = TypeProperties<true>
type IsNumberType<T> = DefinitelyYes<ObjectHasKey<T, TypeProperties<number>>>
type IsStringType<T> = DefinitelyYes<ObjectHasKey<T, TypeProperties<string>>>
type IsBooleanType<T> = DefinitelyYes<ObjectHasKey<T, TypeProperties<boolean>>>
/**
* either: undefined, never, null or {}
* can also be used to check if one union contains one of the above
**/
type IsEmptyType<T> = UnionEmpty<keyof T>
// type IsUndefinedType<T> = Determinate<ObjectHasKeySafe<T, 'valueOf'>>
// And<
// UnionEmpty<keyof T>,
// Not<ObjectHasKeySafe<T, 'valueOf'>>
// >
// false for: undefined, interfaces
// true for: Array, boolean, number, string, Object
type IsScalarOrInstance<T> = ObjectHasKeySafe<T, 'valueOf'>
type X1 = IsEmptyType<'not' | {}>
type ObjectHasKeySafe<O, K extends string> = UnionsOverlap<keyof O, K>;
// type WQ =
type DA = IsNumberType<123>
type DAA = IsStringType<'dup'>
type DAAA = IsEmptyType<{}>
type DAAA2 = UnionEmpty<keyof {}>
type DAAAA = ObjectHasKeySafe<UnionToObject<keyof undefined>, 'valueOf'>
// type DAAA = IsBooleanType<true>
// type Q = true & {lol: 123}
// declare var a : true;
// (a : Q);
type IsScalarType<T> = Or<
IsNumberType<T>,
IsStringType<T>
>
type NonMaybe<T> = {
[P in Purify<keyof T>]: T[P]
}
type Purify<T extends string> = { [P in T]: T }[T]
type NonNull<T> = T & {}
/**
* Notes: use source: { actual: xxx } so we don't get: | undefined
*/
// type A = IsScalarType<boolean>
interface List<T> {
readonly [n: number]: T;
}
/////////
// type Arr = Array<{inner: true}>
// type ExtractArrayType<Arr extends List<T>, T = Arr[-1]> = T //Arr[keyof Arr & '0']
// type ArrType = ExtractArrayType<Arr>
// type SatisfyList = List<void>
/**
* Hidden source property for resolving to the correct type
*/
type SourceReference<T> = {'source'?: {source: T}}
type GetActualSource<
TValue,
SourceParent = NonNull<NonNull<TValue>[keyof NonNull<TValue> & SourceTag]>
> = SourceParent[keyof SourceParent & 'source']
/**
* Resolves the hidden source type
*/
type GetSource<
TValue,
Source = GetActualSource<TValue>
> = If<
IsEmptyType<TValue>,
Source | undefined,
Source
>
type GetSourceIfDefined<T> = If<
HasSourceTag<T>,
GetSource<T>,
T
>;
type RecursivelyExtend<T, AddT> =
If<
IsArrayType<T>,
MapArrayType<T & List<{}>, AddT>,
MapScalarOrObjectType<T, AddT>
> & SourceReference<T>
type MapScalarOrObjectType<T, AddT> =
If<
IsScalarOrInstance<T>, // IsScalarType<T>, //
T,
MapObjectWithPossibleArrays<T, AddT> & SourceReference<T> & AddT
>
type MapObjectWithPossibleArrays<T, AddT> = {
[P in keyof T]: If<
IsArrayType<T[P]>,
MapArrayType<T[P] & List<{}>, AddT> & SourceReference<T[P]>,
MapScalarOrObjectType<T[P], AddT>
> // & AddT
}
type MapArrayType<
Arr extends List<T>,
AddT,
T = Arr[-1],
> =
Array<
MapObjectWithPossibleArrays<T, AddT> & SourceReference<T> & AddT
> & AddT
declare var xil : RecursivelyExtend<DupaArr, {addition: 1}>
declare var xil2 : RecursivelyExtend<Dupa, {addition: 1}>
declare var xil3 : RecursivelyExtend<{a: {b: {c: {d: {x: DupaArr}}}}}, {addition: 1}>
declare var xil4 : RecursivelyExtend<{a: {b: {x: DupaArr}}}, {addition: 1}>
declare var xilple : RecursivelyExtend<{a: {b: string}[]}, {test?: string}>
xilple.a.push({b: 'ab'})
type ForEachAdd<T, AddT> = T & {
[P in keyof T]: ForEachAdd<T[P], AddT>
} & AddT & {'source': T}
declare var bil2 : ForEachAdd<Dupa, {addition: 1}>
type BOL1 = typeof bil2.a['source']
type LOL1 = typeof xil2.a['source']
type LOL0 = typeof xil2.a[0]
type LOL2 = typeof xil3['source']
xil3.a.b.c.d.x[0].b.c
xil4.a.b.x[0].w[0].w[0].w[0].w[0].w[0].w[0].w[0].w[0].x.hix
// xil[0].w[0].
// xil[0]
// xil2.a[0].b
// xil3.a.b.c.d
// xil3.a.b.c.d
// xil2.a[0].
// type WWW = typeof xil
// type MapArrayType<
// Arr extends List<T>,
// AddT,
// // X = {[P in keyof Arr]: Arr[P]} & Arr,
// // T = X[keyof X & '0'],
// T = Arr[-1],
// // Keys extends string = keyof Arr,
// // T = ExtractArrayType<Arr>,
// // Trans = MapToBool<Arr>,
// // IsArray extends Bool = Trans[keyof Trans & 'copyWithin'],
// // W = {
// // [P in keyof T]: T[P] & AddT & MapArrayType<T[P], AddT>
// // } & AddT
// > =
// Array<{
// [P in keyof T]: If<
// // UnionsOverlap<'copyWithin', keyof T[P]>,
// IsArrayType<T[P]>,
// MapArrayType<T[P] & List<void>, AddT>,
// MapObjectType<T[P], AddT>
// > & AddT & SourceReference<T[P]>
// // [P in keyof T]: MapObjectType<T[P], AddT> //& AddT //& MapArrayType<T[P], AddT>
// } & AddT & SourceReference<T>>
// If<
// // UnionContained<keyof Arr, '0'>,
// // UnionHasKey<keyof Arr, '0'>,
// UnionsOverlap<'copyWithin', keyof Arr>,
// // MapToBool<Arr>[keyof MapToBool<Arr> & 'copyWithin'],
// {fuck: true},
// // Array<{
// // [P in keyof T]: T[P] & AddT & MapArrayType<T[P], AddT>
// // } & AddT> & AddT,
// {
// [P in keyof Arr]: Arr[P] & MapArrayType<Arr[P], AddT>
// } & AddT
// >
type DupaArr = Array<{b: {c: 'true'}, x: {hix: 1}, w: DupaArr}>
type Dupa = {a: DupaArr}
declare var dupaarr : MapArrayType<DupaArr, {addition: 1}>
declare var dupa : MapArrayType<Dupa, {addition: 1}>
// type X = typeof dupa.a[0]
// dupa[0].b.c.
// dupa.a[0].
// helpers:
type AllDefinedAsTrue<T> = {
[P in keyof NonMaybe<T>]: '1';
};
type AllUndefinedAsFalse = {
[any: number]: '0';
};
/**
* Due to a bug(?) in TypeScript
* "padding" is needed for the MapToBool helper.
* Otherwise objects with one or zero props return 'any'.
*/
type Padding = {
'____________': '0';
'_____________': '0';
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment