Skip to content

Instantly share code, notes, and snippets.

@KiaraGrouwstra
Last active March 24, 2020 03:17
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save KiaraGrouwstra/be27a32573339ead953a07010ed3b824 to your computer and use it in GitHub Desktop.
Save KiaraGrouwstra/be27a32573339ead953a07010ed3b824 to your computer and use it in GitHub Desktop.
Type-level standard library for TypeScript
// NOTE: code now moved to https://github.com/tycho01/typical
// older revision left here, but it no longer runs well in TS Playground
export type Obj<T> = { [k: string]: T };
export type NumObj<T> = { [k: number]: T };
// export type List = ArrayLike; // no unapplied generic types :(
export type List<T> = ArrayLike<T>;
// progress: https://github.com/Microsoft/TypeScript/issues/16392
export function force<T, V extends T>() {}
// export function force1<T extends '1'>() {}
export type SafeArr<R extends { [i: number]: any }, Name extends string, Param extends number> = R & Array<{[K in Name]: Param }>;
export type SafeObj<O extends Obj<any>, Name extends string> = O & Obj<Name>; // , Param extends string // Obj<{[K in Name]: Param }>
export type SwitchObj<Param extends string, Name extends string, O extends Obj<any>> = SafeObj<O, Name>[Param]; // , Param
// export type SwitchArr<Param extends number, Name extends string, R /*extends List<any>*/> = SafeArr<R, Name, Param>[Param];
// ^ #15768, TS2536 `X cannot be used to index Y` on generic
// ^ comment Array constraint to allow use of Partial<myArr>
// export type ObjectProp<O extends Obj<any>, K extends string> = O[K]; // trivial
export type ObjectHasKey<
O extends {},
K extends string
> = ({[K in keyof O]: '1' } & Obj<'0'>)[K];
force<'1', ObjectHasKey<{a:1}, 'a'>>()
force<'0', ObjectHasKey<{a:1}, 'a'>>()
type oHasOT = ObjectHasKey<{a:1}, 'a'>;
type oHasOF = ObjectHasKey<{a:1}, 'b'>;
type oHasOM = ObjectHasKey<{a?:1}, 'a'>;
type sdkhfjsdkj = Simplify<{a?:1}>
type shjksadsd = {a?:1}['a']
type asdfhjks = keyof {a?:1}
// type oHasRT = ObjectHasKey<myArr, 1>;
// type oHasRF = ObjectHasKey<myArr, -1>;
export type TupleHasIndex<
Arr extends List<any>,
I extends number
> = ({[K in keyof Arr]: '1' } & Array<'0'>)[I];
// ^ #15768, TS2536 `X cannot be used to index Y` on generic
type myArr = [1, 2, 3];
type hasT = TupleHasIndex<myArr, 1>;
type hasF = TupleHasIndex<myArr, -1>;
// type hasT = TupleHasIndex<{a:1}, 'a'>;
// type hasF = TupleHasIndex<{a:1}, 'b'>;
// type hasOT = TupleHasIndex<{ 1: "hi" }, 1>;
// type hasOF = TupleHasIndex<{ 1: "hi" }, -1>;
export type IsArrayType<T> = DefinitelyYes<ObjectHasKey<T, ArrPrototypeMethods>>
type IsArrayT = IsArrayType<myArr>;
type IsArrayR = IsArrayType<any[]>;
type IsArrayO = IsArrayType<{ length: 2 }>;
export type HasKey<T, K> = If<IsArrayType<T>, TupleHasIndex<T, K>, ObjectHasKey<T, K>>;
// ^ Type 'T' does not satisfy the constraint 'any[]'.
// ^ Type 'K' does not satisfy the constraint 'string'.
type HasKeyR = HasKey<any[], 2>;
type HasKeyTT = HasKey<myArr, 2>;
type HasKeyTF = HasKey<myArr, 5>;
type HasKeyOT = HasKey<{ a: 1 }, 'a'>;
type HasKeyOF = HasKey<{ a: 1 }, 'b'>;
// Bool: Not, And, Or, Eq, Neq
export type Not<T extends string> = SwitchObj<T, 'InvalidNotParam', {
'1': '0';
'0': '1';
}>;
// export type Not<T extends string> = { '1': '0'; '0': '1'; };
type not0 = Not<'0'>;
type not1 = Not<'1'>;
type not2 = Not<'2'>;
// type notT = Not<'true'>;
// type notF = Not<'false'>;
type notA = Not<any>;
type notN = Not<never>; // any
// type notT_ = Not<true>; // any
// type notF_ = Not<false>; // any
export type And<
A extends string,
B extends string
> = ({ 1: { 1: '1' } & Obj<'0'> } & Obj<Obj<'0'>>)[A][B];
type andT = And<'1', '1'>;
type andF = And<'0', '1'>;
export type Or<A, B> = ({ 0: { 0: '0' } & Obj<'1'> } & Obj<Obj<'1'>>)[A][B];
type orT = Or<'1', '0'>;
type orF = Or<'0', '0'>;
export type BEq<
A extends string,
B extends string
> = ({ [K in A]: '1' } & Obj<'0'>)[B];
type eq0 = BEq<'0', '0'>;
type eq1 = BEq<'0', '1'>;
type eq2 = BEq<'1', '0'>;
type eq3 = BEq<'1', '1'>;
export type Neq<
A extends string,
B extends string
> = Not<BEq<A, B>>;
// Type: InstanceOf, Matches, Equal
// // throwing Matches
// let f = <T>(v: T) => true;
interface isT<T> {
(v: T): '1';
(v: any): '0';
}
// export type Matches<V, T> = typeof isT<T>(V);
// // ^ this is where things fail until #6606
// let isBool: isT<boolean>
// let falseBool = isBool(false) // 1
// let trueBool = isBool(true) // 1
// let anyBool = isBool(<any> 0) // 0
// let neverBool = isBool(<never> 0) // 1...
// // ^ `never` qualifies as anything... may need to rethink this.
// export type Equal<A, B> = And<Matches<A, B>, Matches<B, A>>;
// export type InstanceOf<V, T> = And<Matches<V, T>, Not<Matches<T, V>>>;
// Casts: ToBool, NumberToString, NumberToBits, BitsToNumber
export type Falsy = undefined | null | 0 | '' | void | never;
// type IsFalsy<V> = Matches<V, Falsy>
// type IsTruthy<V> = Not<IsFalsy<V>>
// type ToBool = IsTruthy
// Bits:
export type ArrayProp<R extends List<any>> = R[-1];
type ArrayPropTest = ArrayProp<Array<0>>;
// export type TupleProp<Arr extends List<any>, I extends number> = SwitchArr<I, 'indexNotInArray', Partial<Arr>>;
// type TuplePropA = TupleProp<[0], 0>;
// type TuplePropB = TupleProp<[0], 1>;
// // type TuplePropC = TupleProp<[], 0>; // A tuple type element list cannot be empty.
// type TuplePropD = TupleProp<number[], 0>;
// single: Length, Left, Right, Invert
// Combine two: BAnd, BOr, Xor
// Combine two, for ordinals like number: Append, Add, Subtract, Multiply, Divide, Modulo, Max, Min
// Ordinal equalities: Gt, Gte, Lt, Lte, Eq
// ^ thru Type equalities? similar but can't do any/never input here
export type Union2Keys<T extends string> = UnionToObject<T> & Obj<never>;
export type Obj2Keys<T> = {[K in keyof T]: K } & Obj<never>;
export type Arr2Keys<T> = {[K in keyof T]: K } & Obj<never>;
export type Keyed<T> = {[K in keyof T]: K };
// need to figure out from the ground up which ops fail for toString:
// keyof - ok
// in - ok
// access - not ok
// {}[|]
// {}[&]
// ({}|{})[]
// ({}&{})[]
// &
// |
export type UnionHasKey<Union extends string, K extends string> = ({[S in Union]: '1' } & Obj<'0'>)[K];
// export type UnionHasKey<Union extends string, K extends string> = And<({[S in Union]: '1' } & Obj<'0'>)[K], ({[S in K]: '1' } & Obj<'0'>)[Union]>;
type UnionHasT = UnionHasKey<'a'|'b', 'b'>;
type UnionHasF = UnionHasKey<'a'|'b', 'c'>;
type UnionHasTT = UnionHasKey<'a'|'b', 'a'|'b'>;
type UnionHasFF = UnionHasKey<'a'|'b', 'c'|'d'>;
type UnionHasTF = UnionHasKey<'a'|'b', 'b'|'c'>;
// v these fail hard:
type UnionHasTS = UnionHasKey<'a'|'b'|'toString', 'toString'>;
type UnionHasFS = UnionHasKey<'a'|'b', 'toString'>;
type skjdfds = keyof Simplify<{ [P in ('a' | 'b') & ('b' | 'c')]: "1" }>
export type Indeterminate<T extends string> = And<ObjectHasKey<UnionToObject<T>, '0'>, ObjectHasKey<UnionToObject<T>, '1'>>;
export type Determinate<T extends string> = Not<Indeterminate<T>>;
export type DefinitelyYes<T extends string> = And<T, Determinate<T>>;
export type DefinitelyNo<T extends string> = And<Not<T>, Determinate<T>>;
export type If<Cond extends '0' | '1', Then, Else> = { 1: Then, 0: Else }[Cond];
export type UnionToObject<Keys extends string> = { [K in Keys]: K };
type TestUnion2Obj = Obj2Keys<UnionToObject<'b' | 'c'>>;
export type IntersectionUnions<Big extends string, Small extends string> = Obj2Keys<UnionToObject<Small>>[Big] /*!*/;
// type TestUnionInt2 = IntersectionUnions<1|2|3, 2|3|4>; // only strings allowed
type TestUnionInt1T = IntersectionUnions<'a' | 'b' | 'c', 'b' | 'c' | 'd'>;
type TestUnionInt1F = IntersectionUnions<'a' | 'b' | 'c', 'x' | 'z' | 'd'>;
// type sdkflsd = {a:1}[never]
// how do I may string/never to 1/0?
export type UnionContained<T extends string, U extends string> = DefinitelyYes<({ [P in U]: '1' } & Obj<'0'>)[T | U]>;
type ijdkj1 = UnionContained<never, 'a'>;
type ijdkj2 = UnionContained<'a', 'a'>;
type ijdkj3 = UnionContained<'b', 'a'>;
type ijdkj4 = UnionContained<'a' | 'b', 'a'>;
export type UnionEmpty<T extends string> = And<UnionContained<T, 'a'>, UnionContained<T, 'b'>>;
type EmptyT = UnionEmpty<never>;
type EmptyF = UnionEmpty<'a'>;
type EmptyF2 = UnionEmpty<'c'>;
type EmptyFF = UnionEmpty<'a' | 'b'>;
type EmptyFFF = UnionEmpty<'a' | 'b' | 'c' | 'd'>;
type EmptyFF2 = UnionEmpty<'c' | 'd'>;
type EmptyS = UnionEmpty<'toString'>;
type EmptyS2 = UnionEmpty<'toString' | 'a'>;
export type UnionsOverlap<Big extends string, Small extends string> = Not<UnionEmpty<IntersectionUnions<Big, Small>>>;
type OverlapT = UnionsOverlap<'a', 'a' | 'b'>;
type OverlapF = UnionsOverlap<'a', 'c'>;
export type ObjectHasKeySafe<O extends object, K extends string> = UnionsOverlap<keyof O, K>;
type ObjectHasKeySafeT = ObjectHasKeySafe<{ a: 1 }, 'a'>;
type ObjectHasKeySafeF = ObjectHasKeySafe<{ a: 1 }, 'b'>;
type ObjectHasKeySafeTF = ObjectHasKeySafe<{ a: 1 }, 'a' | 'b'>; // 1
type ObjectHasKeySafeST = ObjectHasKeySafe<{ a: 1, toString(): string }, 'a'>;
type ObjectHasKeySafeSF = ObjectHasKeySafe<{ a: 1, toString(): string }, 'b'>; // 1 :() //////////////////////////////////////////
type ObjectHasKeySafeSTF = ObjectHasKeySafe<{ a: 1, toString(): string }, 'a' | 'b'>; // 1
type ObjectHasKeySafeTS = ObjectHasKeySafe<{ a: 1 }, 'toString'>;
type ObjectHasKeySafeFST = ObjectHasKeySafe<{ a: 1 }, 'toString' | 'a'>; // 1
type ObjectHasKeySafeFS = ObjectHasKeySafe<{ a: 1 }, 'toString' | 'b'>;
type ObjectHasKeySafeTSF = ObjectHasKeySafe<{ a: 1 }, 'toString' | 'a' | 'b'>;
type ObjectHasKeySafeSST = ObjectHasKeySafe<{ a: 1, toString(): string }, 'toString'>;
type ObjectHasKeySafeSSTA = ObjectHasKeySafe<{ a: 1, toString(): string }, 'toString' | 'a'>;
type ObjectHasKeySafeSSF = ObjectHasKeySafe<{ a: 1, toString(): string }, 'toString' | 'b'>;
type ObjectHasKeySafeSSTF = ObjectHasKeySafe<{ a: 1, toString(): string }, 'toString' | 'a' | 'b'>;
export type ObjProp<O extends Obj<any>, K extends string, Default = never> = If<ObjectHasKeySafe<O, K>, O[K], Default>;
type ObjPropA = ObjProp<{ a: 1 }, 'a'>;
type ObjPropB = ObjProp<{ a: 1 }, 'b'>;
type ObjPropC = ObjProp<{ a: 1 }, 'toString'>;
type ObjPropD = ObjProp<{ a: 1, toString(): string }, 'a'>;
type ObjPropE = ObjProp<{ a: 1, toString(): string }, 'b'>; // any :( //////////////////////////
type ObjPropF = ObjProp<{ a: 1, toString(): string }, 'toString'>;
// export type IsUnion<T extends string> = { [P in T]: UnionContained<T, P> }//[T];
export type IsUnion<
T extends string,
O extends { [P in T]: UnionContained<T, P> }
= { [P in T]: UnionContained<T, P> }
> = O[T];
type IsUnionT = IsUnion<'a' | 'b'>;
type IsUnionF = IsUnion<'a'>;
type IsUnionFF = IsUnion<never>;
// type IsUnionU = IsUnion<'a' | undefined>;
// export type Lambda<K> = any;
// export type TypeMap<F extends Lambda, Keys extends string> = Pick<F, Keys>[Keys];
// // nope, no type lambdas -- can't have unapplied types
// https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458
// type sfsd = { [P in PrototypeMethods]: P; [k: string]: never; };
type sdkjdsl = { toString: "toString"; [P in keyof "a"]: never };
type sdkjds2l = { [P in keyof U]: never; [K in keyof PrototypeMethods]: K; };
export type Diff<T extends string, U extends string> =
({[P in T]: P } &
{ [P in U]: never } & // toString: "toString"; toLocaleString: "toLocaleString";
{ [k: string]: never })[T]; // needs TS 2.5 // toString: "toString"; toLocaleString: "toLocaleString";
type TestDiff = Diff<"a" | "b" | "c", "c" | "d">; // "a" | "b"
type sdfslj = Diff<'a' | 'b' | 'toString', 'a'>;
type sdfslk = Diff<'a' | 'b', 'a' | 'toString'>;
export type UnionDiff_<Big extends string, Small extends string> =
{[K in Big]: { 1: Union2Keys<Big>[K], 0: never }[Not<UnionHasKey<Small, K>>]}//[Big];
export type UnionDiff<
Big extends string,
Small extends string,
Step extends UnionDiff_<Big, Small> = UnionDiff_<Big, Small>
> = Step[Big];
type TestUnionDiff = UnionDiff<'a' | 'b' | 'c', 'b' | 'c' | 'd'>;
type sdgslj = UnionDiff<'a' | 'b' | 'toString', 'a'>;
type sdgslk = UnionDiff<'a' | 'b', 'a' | 'toString'>;
type sdgslk2 = ObjectHasKey<{ a: 1 }, "toString">;
type Item1 = { a: string, b: number, c: boolean };
type Item2 = { a: number };
export type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>; // {[P in Diff<keyof T, K>]: T[P]}
type OmitTest = Omit<Item1, "a"> // { b: number, c: boolean };
export type Overwrite<K, T> = {[P in keyof T | keyof K]: { 1: T[P], 0: K[P] }[ObjectHasKey<T, P>]};
// export type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;
// ^ no-dependency version by Anders, uses intersection
// export type Overwrite<T, U, Int = { [P in Diff<keyof T, keyof U>]: T[P] } & U> = Pick<Int, keyof Int>;
// ^ my attempt at cleaning out the intersection, somehow makes FromPairs/ZipObject fail
type TestOverwrite = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };
// keys optional in 2nd arg: works without `strictNullChecks`
// intersection objs
export type IntersectionObjectKeys<A, B> = Pick<Obj2Keys<B>, keyof A>[keyof A];
export type IntersectionObjects<A, B> = Pick<A, IntersectionObjectKeys<A, B>>;
type Obj1 = { a: 1, b: 2 };
type Obj2 = { b: 'X', c: 'Z' };
type Int1 = IntersectionObjects<Obj1, Obj2>;
// { b: 2; }
type Int2 = IntersectionObjects<Obj2, Obj1>;
// { b: 'X'; }
export type ObjectValsToUnion<O> = O[keyof O];
type ObjectValsToUnionTest = ObjectValsToUnion<Obj1>;
// try for array with head so as to implement tail, array iteration, reduce, and traversables?
type PrototypeMethods = 'toLocaleString' | 'toString' //| 'constructor' | 'hasOwnProperty' | 'isPrototypeOf' | 'propertyIsEnumerable' | 'valueOf' | '__defineGetter__' | '__defineSetter__' | '__lookupGetter__' | '__lookupSetter__' | '__proto__';
type Prototype = {[K in PrototypeMethods]: K };
// type PrototypeHas<K extends string> = ObjectHasKey<Prototype, K>;
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'
// 'toLocaleString' | 'toString' |
type ArrPrototype = {[K in ArrPrototypeMethods]: K };
type ArrPrototypeHas<K extends string> = ObjectHasKey<ArrPrototype, K>;
// difference arrs
export type NumberToString = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','96','97','98','99','100','101','102','103','104','105','106','107','108','109','110','111','112','113','114','115','116','117','118','119','120','121','122','123','124','125','126','127','128','129','130','131','132','133','134','135','136','137','138','139','140','141','142','143','144','145','146','147','148','149','150','151','152','153','154','155','156','157','158','159','160','161','162','163','164','165','166','167','168','169','170','171','172','173','174','175','176','177','178','179','180','181','182','183','184','185','186','187','188','189','190','191','192','193','194','195','196','197','198','199','200','201','202','203','204','205','206','207','208','209','210','211','212','213','214','215','216','217','218','219','220','221','222','223','224','225','226','227','228','229','230','231','232','233','234','235','236','237','238','239','240','241','242','243','244','245','246','247','248','249','250','251','252','253','254','255'];
// ^ R.pipe(R.range(0), R.map(JSON.stringify), JSON.stringify, R.replace(/"/g,"'"))(256)
type NumberToStringTest = NumberToString[3];
type NumberToStringTest2 = NumberToString['3'];
// type NumberToString<I extends number> = SafeArr<['0', '1', '2', '3', '4', '5'], 'invalidIndex', I>;
// type NumberToStringTest = NumberToString<3>;
// export type StringToNumber = {[N in number]: N };
export type StringToNumber = {0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,19:19,20:20,21:21,22:22,23:23,24:24,25:25,26:26,27:27,28:28,29:29,30:30,31:31,32:32,33:33,34:34,35:35,36:36,37:37,38:38,39:39,40:40,41:41,42:42,43:43,44:44,45:45,46:46,47:47,48:48,49:49,50:50,51:51,52:52,53:53,54:54,55:55,56:56,57:57,58:58,59:59,60:60,61:61,62:62,63:63,64:64,65:65,66:66,67:67,68:68,69:69,70:70,71:71,72:72,73:73,74:74,75:75,76:76,77:77,78:78,79:79,80:80,81:81,82:82,83:83,84:84,85:85,86:86,87:87,88:88,89:89,90:90,91:91,92:92,93:93,94:94,95:95,96:96,97:97,98:98,99:99,100:100,101:101,102:102,103:103,104:104,105:105,106:106,107:107,108:108,109:109,110:110,111:111,112:112,113:113,114:114,115:115,116:116,117:117,118:118,119:119,120:120,121:121,122:122,123:123,124:124,125:125,126:126,127:127,128:128,129:129,130:130,131:131,132:132,133:133,134:134,135:135,136:136,137:137,138:138,139:139,140:140,141:141,142:142,143:143,144:144,145:145,146:146,147:147,148:148,149:149,150:150,151:151,152:152,153:153,154:154,155:155,156:156,157:157,158:158,159:159,160:160,161:161,162:162,163:163,164:164,165:165,166:166,167:167,168:168,169:169,170:170,171:171,172:172,173:173,174:174,175:175,176:176,177:177,178:178,179:179,180:180,181:181,182:182,183:183,184:184,185:185,186:186,187:187,188:188,189:189,190:190,191:191,192:192,193:193,194:194,195:195,196:196,197:197,198:198,199:199,200:200,201:201,202:202,203:203,204:204,205:205,206:206,207:207,208:208,209:209,210:210,211:211,212:212,213:213,214:214,215:215,216:216,217:217,218:218,219:219,220:220,221:221,222:222,223:223,224:224,225:225,226:226,227:227,228:228,229:229,230:230,231:231,232:232,233:233,234:234,235:235,236:236,237:237,238:238,239:239,240:240,241:241,242:242,243:243,244:244,245:245,246:246,247:247,248:248,249:249,250:250,251:251,252:252,253:253,254:254,255:255};
// ^ R.pipe(R.range(0), R.map(k => [k,k]), R.fromPairs, JSON.stringify, R.replace(/"/g,''))(256)
type StringToNumberTest = StringToNumber['3'];
export type Inc = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256];
// R.pipe(R.range(0), R.map(R.inc), JSON.stringify)(256)
export type Dec = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254];
// R.pipe(R.range(0), R.map(R.dec), JSON.stringify)(256)
// export type Dec = { 0: -1, 1: 0, 2: 1, 3: 2, 4: 3 };
export type Add<
A extends number,
B extends number
> = { 1: A, 0: Add<Inc[A], Dec[B]> }[NumbersEqual<0, B>];
type TestAdd = Add<3, 2>;
export type Subtract<
A extends number,
B extends number
> = { 1: A, 0: Subtract<Dec[A], Dec[B]> }[NumbersEqual<0, B>];
type TestSubtract = Subtract<3, 2>;
export type Mult<
A extends number,
B extends number,
Acc extends number = 0
> = { 1: Acc, 0: Mult<A, Dec[B], Add<Acc, A>> }[NumbersEqual<0, B>];
type TestMult = Mult<3, 2>;
export type Pow<
Base extends number,
Exp extends number,
Acc extends number = 1
> = { 1: Acc, 0: Pow<Base, Dec[Exp], Mult<Acc, Base>> }[NumbersEqual<0, Exp>];
type TestPowA = Pow<3, 2>;
type TestPowB = Pow<2, 3>;
export type DivFloor<
A extends number,
B extends number,
Acc extends number = 0
> = { 0: Acc, 1: DivFloor<Subtract<A, B>, B, Inc[Acc]> }[Gt<A, B>];
type TestDivFloor = DivFloor<5, 2>;
export type Modulo<
A extends number,
B extends number,
Acc extends number = 0
> = { 0: A, 1: Modulo<Subtract<A, B>, B, Inc[Acc]> }[Gt<A, B>];
type TestModulo = Modulo<5, 2>;
export type Gt<
A extends number,
B extends number
> = {
1: '0',
0: If<NumbersEqual<0, B>, '1', Gt<Dec[A], Dec[B]>>,
}[NumbersEqual<0, A>];
type TestGtG = Gt<3, 2>;
type TestGtL = Gt<2, 3>;
type TestGtE = Gt<3, 3>;
export type Lt<
A extends number,
B extends number
> = {
1: '0',
0: If<NumbersEqual<0, A>, '1', Lt<Dec[A], Dec[B]>>,
}[NumbersEqual<0, B>];
type TestLtG = Lt<3, 2>;
type TestLtL = Lt<2, 3>;
type TestLtE = Lt<3, 3>;
export type Gte<
A extends number,
B extends number
> = {
1: '1',
0: If<NumbersEqual<0, A>, '0', Gte<Dec[A], Dec[B]>>,
}[NumbersEqual<0, B>];
type TestGteG = Gte<3, 2>;
type TestGteL = Gte<2, 3>;
type TestGteE = Gte<3, 3>;
export type Lte<
A extends number,
B extends number
> = {
1: '1',
0: If<NumbersEqual<0, B>, '0', Lte<Dec[A], Dec[B]>>,
}[NumbersEqual<0, A>];
type TestLteG = Lte<3, 2>;
type TestLteL = Lte<2, 3>;
type TestLteE = Lte<3, 3>;
export type FirstIndex<R extends NumObj<any>, I extends number = 0> =
{ 1: I, 0: FirstIndex<R, Inc[I]> }[ObjectHasKey<R, I>];
type TestFirstIndex = FirstIndex<{ 2: 'a', 3: 'b' }>;
export type IncIndex<R extends List<any>, N extends number, I extends number = 0 /*FirstIndex<R>*/, Acc = { length: R['length'] }> =
{ 0: Acc, 1: IncIndex<R, N, Inc[I], Acc & { [P in NumberToString[Add<I, N>]]: R[I] }> }[ObjectHasKey<R, I>];
type TestIncIndex = IncIndex<{ 0: 'a', 1: 'b', length: 2 }, 2>;
export type DecIndex<R extends List<any>, N extends number, I extends number = FirstIndex<R>, Acc = { length: R['length'] }> =
{ 0: Acc, 1: DecIndex<R, N, Inc[I], Acc & { [P in NumberToString[Subtract<I, N>]]: R[I] }> }[ObjectHasKey<R, I>];
type TestIncIndex = DecIndex<{ 2: 'a', 3: 'b', length: 2 }, 2>;
export type ZeroIndex<R extends List<any>, I extends number = FirstIndex<R>> = /*If<ObjectHasKey<R, 0>, R,*/ DecIndex<R, I, I>//>;
type TestIncIndex = ZeroIndex<{ 2: 'a', 3: 'b', length: 2 }, 2>;
type TestArr = ['a', 'b', 'c'];
export type TupleToObject<R extends List<any>, I extends number = 0, Acc = {}> =
{ 1: TupleToObject<R, Inc[I], Acc & { [P in NumberToString[I]]: R[I] }>, 0: Simplify<Acc> }[TupleHasIndex<R, I>];
type Arr2ObjTest = TupleToObject<TestArr>;
export type TupleToList<R extends List<any>, I extends number = 0, Acc = {}> =
{ 1: TupleToList<R, Inc[I], Acc & { [P in NumberToString[I]]: R[I] }>, 0: Simplify<Acc & { length: I }> }[TupleHasIndex<R, I>];
type Arr2LikeTest = TupleToList<TestArr>;
// export type NumObjToList<O> = Length<O>;
export type NumObjToList<O extends NumObj<any>> = O & { length: Length<O> };
// export type NumObjToList<O extends NumObj<any>> = Overwrite<O, { length: Length<O> }>;
// export type NumObjToList<O extends NumObj<any>, Len extends Length<O> = Length<O>> = O & { length: Len };
// export type NumObjToList<O extends NumObj<any>, Len extends Length<O> = Length<O>> = Overwrite<O, { length: Len }>;
type TestNum2LikeA = NumObjToList<{ 0: 'a', 1: 'b' }>;
type TestNum2LikeB = NumObjToList<TupleToObject<TestArr>>;
// ^ WRONG, length gives 0, see #16018
export type ListToNumObj<R extends List<any>> = Omit<R, 'length'>;
type TestListToNumObj = ListToNumObj<{ 0: 'a', 1: 'b', length: 2 }>;
export type AppendList<
R extends List<any>,
T,
Len extends number = LengthList<R>
> = Overwrite<R & { [P in NumberToString[Len]]: T }, { length: Inc[Len] }>;
type TestAppendList = AppendList<{ 0: 'a', 1: 'b', length: 2 }, 'c'>;
export type AppendNumObj<
R extends NumObj<any>,
T,
Len extends number = Length<R>
> = R & { [P in NumberToString[Len]]: T };
type TestAppendNumObj = AppendNumObj<{ 0: 'a', 1: 'b' }, 'c'>;
// ^ WRONG
export type Prepend<
R extends List<any>,
T
> = { 0: T } & IncIndex<R, 1>;
// presumes the list is already zero-indexed, otherwise needs ZeroIndex
type TestPrepend = Prepend<{ 0: 'b', 1: 'c', length: 2 }, 'a'>;
export type ConcatLists<A extends List<any>, B extends List<any>> =
Overwrite<A & IncIndex<B, A['length']>, { length: Add<A['length'], B['length']> }>;
type TestConcatLists = ConcatLists<{ 0: 'a', 1: 'b', length: 2 }, { 0: 'c', 1: 'd', length: 2 }>;
export type ConcatNumObjs<A extends NumObj<any>, B extends NumObj<any>> =
A & IncIndex<B, Length<A>;
type TestConcatNumObjs = ConcatNumObjs<{ 0: 'a', 1: 'b' }, { 0: 'c', 1: 'd' }>;
export type TupleToUnion<R extends List<any>, I extends number = 0, Acc = never> =
{ 1: TupleToUnion<R, Inc[I], Acc | R[I]>, 0: Acc }[TupleHasIndex<R, I>];
type Arr2UnionTest = TupleToUnion<TestArr>;
export type TupleIndicesToUnion<R extends List<any>, I extends number = 0, Acc = never> =
{ 1: TupleIndicesToUnion<R, Inc[I], Acc | I>, 0: Acc }[TupleHasIndex<R, I>];
type ArrKeys2UnionTest = TupleIndicesToUnion<TestArr>;
export type LengthList<R extends List<any>> = R['length'];
type TestVectorLength = LengthList<{ 0: 'a', 1: 'b', length: 2 }>;
export type Length<R extends NumObj<any>, I extends number = 0> =
{ 1: Length<R, Inc[I]>, 0: I }[ObjectHasKey<R, I>];
type TestObjLength = Length<TupleToObject<TestArr>>;
type TestTplLength = Length<TestArr>;
type TestArrLength = Length<any[]>;
type TestNumLength = Length<{ 0: 'a', 1: 'b' }>;
export type Vector<T, N extends number, I extends number = 0, Acc = { length: N }> =
{ 1: Acc, 0: Vector<T, N, Inc[I], Acc & { [P in NumberToString[I]]: T }> }[NumbersEqual<I, N>];
type TestVector = Vector<number, 3>;
// export type TupleLastIndex<R extends {}> = Dec[Length<R>];
export type TupleLastIndex<R extends List<any>, I extends number = 0> =
{ 1: TupleLastIndex<R, Inc[I]>, 0: Dec[I] }[TupleHasIndex<R, I>];
type TestLast = TupleLastIndex<TestArr>;
type TestLastArr = TupleLastIndex<any[]>;
export type TupleLastElem<R extends List<any>, I extends number = 0> =
{ 1: TupleLastElem<R, Inc[I]>, 0: R[Dec[I]] }[TupleHasIndex<R, I>];
type TestLastElem = TupleLastElem<TestArr>;
// v useless type to demo backward iteration
export type FirstElem<R extends List<any>, I extends number = Length<R>, Prev extends number = Dec[I]> =
{ 1: FirstElem<R, Prev>, 0: R[I] }[TupleHasIndex<R, Prev>];
// ^ #15768, TS2536 `X cannot be used to index Y` on generic
type TestFirstElem = FirstElem<TestArr>;
// v useless type to demo backward iteration
export type FirstIndex<R extends List<any>, I extends number = TupleLastIndex<R>> =
{ 1: FirstIndex<R, Dec[I]>, 0: Inc[I] }[TupleHasIndex<R, I>];
// ^ Generic type instantiation is excessively deep and possibly infinite
type TestFirstIndex = FirstIndex<TestArr>;
// ^ WRONG
// Ramda functions listed in #12512 redone with iteration:
export type PathFn<T, R extends List<string|number>, I extends number = 0> =
{ 1: PathFn<T[R[I]], R, Inc[I]>, 0: T }[TupleHasIndex<R, I>];
type PathTest = PathFn<{ a: { b: ['c', { d: 'e' }] } }, ['a', 'b', 1, 'd']>; // "e"
export declare function path<T, R extends List<string|number>>(obj: T, path: R): PathFn<T, R>;
let par1: { a: { b: ['c', { d: 'e' }] } };
let par2: ['a', 'b', 1, 'd'];
const pathTest = path(par1, par2);
// fails :(, should also yield "e"
export type PathOrFn<T, Def, R extends List<string|number>, I extends number = 0> =
{ 1: If<HasKey<T, R[I]>, PathOrFn<T[R[I]], Def, R, Inc[I]>, Def>, 0: T }[TupleHasIndex<R, I>];
type PathOrTestT = PathOrFn<{ a: { b: ['c', { d: 'e' }] } }, "oh", ['a', 'b', 1, 'd']>; // "e"
type PathOrTestF = PathOrFn<{ a: { b: ['c', { d: 'e' }] } }, "oh", ['a', 'b', 4]>; // "oh"
export type MergeAllFn<R extends List<Obj<any>>, I extends number = 0, T = {}> =
{ 1: MergeAllFn<R, Inc[I], Overwrite<T, R[I]>>, 0: T }[TupleHasIndex<R, I>];
type MergeAllTest = MergeAllFn<[{ a: 1, b: 2 }, { b: 3, c: 4 }, { d: 5 }]>; // { a: 1, b: 3, c: 4, d: 5 }
export type FromPairsFn<R extends List<[string|number, any]>, I extends number = 0, T = {}> =
{ 1: FromPairsFn<R, Inc[I], Overwrite<T, { [P in R[I][0]]: R[I][1] }>>, 0: T }[TupleHasIndex<R, I>];
type FromPairsTest = FromPairsFn<[['a', 1], ['b', 2], ['c', 3], ['a', 5]]>;
// { a: 5, b: 2, c: 3 }
export type ZipObjectFn<R extends List<string>, R2 extends List<any>, I extends number = 0, T = {}> =
{ 1: ZipObjectFn<R, R2, Inc[I], Overwrite<T, { [P in R[I]]: R2[I] }>>, 0: T }[TupleHasIndex<R, I>];
type ZipObjectTest = ZipObjectFn<['a', 'b', 'c'], [1, 2, 3]>;
// { a: 1, b: 2, c: 3 }
export type ListFrom<
R extends List<any>,
N extends number,
I extends number = N,
Acc extends List<any> = { length: Subtract<Length<R>, I> }
> = { 0: Acc, 1: ListFrom<R, N, Inc[I], Acc & { [P in NumberToString[Subtract<I, N>]]: R[I] }> }[TupleHasIndex<T, I>];
type TestListFrom = ListFrom<{ 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }, 2>;
export type ListTo<
R extends List<any>,
N extends number,
I extends number = 0,
Acc extends List<any> = { length: N }
> = { 0: Acc, 1: ListTo<R, N, Inc[I], Acc & { [P in NumberToString[I]]: R[I] }> }[NumbersEqual<I, N>];
type TestListTo = ListTo<{ 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }, 2>;
export type ListTail<R extends List<any>> = ListFrom<R, 1>;
type TestListTail = ListTail<{ 0: 'a', 1: 'b', 2: 'c', length: 3 }>;
export type DifferenceLists<Big extends List<any>, Small extends List<any>> = ListFrom<Big, ListLength<Small>>;
type TestDifferenceLists = DifferenceLists<{ 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }, { 0: 123, 1: 456, length: 2 }>;
// `...` needs #5453, so syntax errors:
// export type TupleFrom<T extends List<any>, I extends number, Acc extends List<any> = []> =
// { 0: Acc, 1: TupleFrom<T, Inc[I], [...Acc, T[I]]> }[TupleHasIndex<T, I>];
// type TestTupleFrom = TupleFrom<['a', 'b', 'c', 'd'], 2>;
// export type TupleTo<T extends List<any>, To extends number, I extends number = 0, Acc extends List<any>=[]> =
// If<TupleHasIndex<T, I>, { 0: Acc; 1: TupleTo<T, To, Inc[I], [...Acc, T[I]]>; }[NumbersEqual<I, To>], T>;
// type TestTupleTo = TupleTo<['a', 'b', 'c', 'd'], 2>;
// export type TupleTail<R extends List<any>> = TupleFrom<R, 1>;
// type TestTupleTail = TupleTail<['a', 'b', 'c']>;
// export type DifferenceTuples<Big extends List<any>, Small extends List<any>> = TupleFrom<Big, Length<Small>>;
// type TestDifferenceTuples = DifferenceTuples<['a', 'b', 'c', 'd'], [123, 456]>;
export type StringsEqual<
A extends string,
B extends string
> = ObjectHasKey<{[P in A]: 'hi' }, B>;
type StringsEqualT = StringsEqual<'a', 'a'>;
type StringsEqualF = StringsEqual<'a', 'b'>;
// type NumbersEqual<A extends number, B extends number> = ObjectHasKey<{ [P in NumberToString[A]]: 'hi' }, NumberToString[B]>;
export type NumbersEqual<
A extends number,
B extends number
> = DefinitelyYes<StringsEqual<NumberToString[A], NumberToString[B]>>;
// ^ #15768, TS2536 `X cannot be used to index Y` on generic
type NumbersEqualT = NumbersEqual<123, 123>;
type NumbersEqualF = NumbersEqual<123, 456>;
// type NumbersEqualTA = NumbersEqual<123, '123'>;
// type NumbersEqualTB = NumbersEqual<'123', 123>;
type NumbersEqualFC = NumbersEqual<0, 5>;
type NumbersEqualFD = NumbersEqual<5, 0>;
export type Simplify<T> = Pick<T, keyof T>;
type abc = { a: 1, b: 2, foo: () => string, toString: () => string, toLocaleString: () => string }
type bcd = Omit<abc, "b">;
type bdd = Omit<abc, "toString">;
type ccd = Simplify<abc & { "b": never }>;
type ccg = Simplify<abc & { "b": any }>;
type crd = Simplify<abc & { "toString": never }>;
type crg = Simplify<abc & { "toString": any }>;
type cqd = Simplify<abc & { "foo": never }>;
type cqg = Simplify<abc & { "foo": any }>;
type dcd = Overwrite<abc, { "b": never }>;
type dcg = Overwrite<abc, { "b": any }>;
type dcf = Overwrite<abc, { "toString": never }>;
type dch = Overwrite<abc, { "toString": any }>;
type duf = Overwrite<abc, { "foo": never }>;
type duh = Overwrite<abc, { "foo": any }>;
type cdd = Simplify<abc & { "toString": never }>;
type bce = Omit<myArr, "length">;
type ItemA = { a: string, b: number, c: boolean, toString(): string };
type KeyedItem1 = keyof ItemA
type DiffedItem1 = Diff<keyof ItemA, "a">
type OmittedItem1 = Omit<ItemA, "a">
// should be { b: number, c: boolean, toString(): string }
// is { b: number; c: boolean; }
type OmittedItem2 = keyof Omit<ItemA, "a">
// "b" | "c" | ("toString" & (() => string))
// ^ it actually lied about "toString" being gone; it's just no longer showing because its key is somehow no longer just a string?!
// #16072
declare function comp<A, B, C>(f: (x: A) => B, g: (x: B) => C): (x: A) => C
const wrapper = comp(x => [x], y => ({ p: y })); // (x: {}) => { p: {}[] }
// #12838
declare function inc(n: number): number; // +1
declare function identity<T>(a: T): T;
declare function compose<V0, T1>(fn0: (x0: V0) => T1): (x0: V0) => T1;
declare function compose<V0, T1, T2>(fn1: (x: T1) => T2, fn0: (x: V0) => T1): (x: V0) => T2;
declare function pipe <V0, T1>(fn0: (x0: V0) => T1): (x0: V0) => T1; // arity 1: same as compose
declare function pipe <V0, T1, T2>(fn0: (x: V0) => T1, fn1: (x: T1) => T2): (x: V0) => T2; // arity 2: params swapped
// don't use generics if it can't resolve them right away:
compose(identity) // generics lost, now {} => {}
pipe (identity) // ditto
// argument order apparently matters too:
pipe (inc, identity); // ok, number -> number
compose(identity, inc); // nope, number => {}
// also no reasoning backward:
compose(inc, identity); // {} => number
pipe (identity, inc); // {} => number
type AddPrototype<T> = Pick<T, keyof T>;
type ArrProto<T extends List<any>> = AddPrototype<T> & {
[Symbol.iterator]: () => IterableIterator<T[-1]>,
[Symbol.unscopables]: () => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }
} & { [i: number]: T[-1] };
// #6229
const foo: [1, "a"] = [1, "a"]; // no cast with #16389
var t3: TupleToObject<typeof foo> = foo;
t3[2]; // error
var t1: [number, string] = <ArrProto<[1, "a"]>>[1, "a"];
// ^ ok
// v make either of these error:
var t5a: [number, string] = [1, "a", "b"];
var t5b: [number, string] = <ArrProto<[1, "a", "b"]>> [1, "a", "b"];
var t5c: TupleToObject<[number, string]> = [1, "a", "b"];
var t5d: TupleToObject<[number, string]> = <TupleToObject<[1, "a", "b"]>> [1, "a", "b"];
var t5e: TupleToObject<[number, string]> = <ArrProto<[1, "a", "b"]>> [1, "a", "b"];
// test interplay types
type asdfInt = ({ a: 1 } & { c: 4 } & { b: 2, a: string } & { [k: string]: 3 })['a']
// objects considered merged, indexes ignored but real results intersect'd
type asdfUnn = ({ a: 1 } /*| { c: 4 }*/ | { b: 2, a: string } /*| { [k: string]: 3 }*/)['a'];
// ^ union of results; each object must *explicitly* contain key, no indexes
type afdsfUnn = { a: 1, b: 2, c: 3 }['a' | 'b' /*| 'd'*/];
// ^ union of results, keys must all be contained
type afdsfInt = { a: 1, b: 2, c: 3 }['a' /*& 'b'*/]; // impossible key
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment