Created
November 26, 2017 12:20
-
-
Save niieani/37a872f808c34049523ea0f7252e9019 to your computer and use it in GitHub Desktop.
TypeScript utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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