Yet another typing library.
This differs by aiming to be less experimental than others, driven by industry use cases.
Many of the exposed types are a very thin layer above built in functionality.
The goal is to provide all of the building blocks necessary to make concise, yet complex types.
test('Can make all fields of options object required (not optional and not nullable)',t=>{
typex={a?:string,b:number|undefined};
typegot=AllRequired<x>;
typeexpected={a:string,b:number};
assert<got,expected>(t);
assert<expected,got>(t);
});
CombineObjects
Takes two objects and returns their intersection.
This combines all keys and uses ObjectType to "clean up" the resultant object.
Useful for making extremely complex types look nice in VSCode.
test('Can combine two objects (without pesky & in vscode)',t=>{
typea={x:number,y:'hi'};
typeb={z:number};
typegot=CombineObjects<a,b>;
typeexpected={
x:number,
y:'hi',
z:number
};
assert<got,expected>(t);
assert<expected,got>(t);
});
DeepPartial
Uses Partial to make every parameter of an object optional (| undefined).
Iterates through arrays of objects and nested objects.
test('Can get a deep partial object',t=>{
typea={
b:{
c:number
},
d:string
};
typegot=DeepPartial<a>;
typeexpected={
b?:{
c?:number
},
d?:string
};
assert<got,expected>(t);
assert<expected,got>(t);
});
test('Can get a deep partial object with arrays',t=>{
typea={
b:Array<{
c:number,
}>,
};
typegot=DeepPartial<a>;
typeexpected={
b?:Array<{
c?:number,
}>,
};
assert<got,expected>(t);
assert<expected,got>(t);
});
test('Can get a deep partial object with functions',t=>{
typex={
a:()=>22,
b:string,
c:{
d:number,
},
};
typeexpected={
a?:()=>22,
b?:string,
c?:{
d?:number,
},
};
typegot=DeepPartial<x>;
assert<got,expected>(t);
assert<expected,got>(t);
});
DeepReadonly
Uses Readonly to make every parameter of an object - and its sub-objects recursively - readonly.
test('Can make an object with functions readonly',t=>{
typex={
a:()=>22,
b:string,
c:{
d:boolean,
},
};
typeexpected={
readonlya:()=>22,
readonlyb:string,
readonlyc:{
readonlyd:boolean,
},
};
typegot=DeepReadonly<x>;
assert<got,expected>(t);
assert<expected,got>(t);
});
DiffKeys
Gets all of the keys that are different between two objects.
This is a set difference between keyof T and keyof U.
Note that calling this with arguments reversed will have different results.
test('Can get all keys that are different between objects',t=>{
typea={x:number,y:string};
typeb={y:string,z:number};
typegotA=DiffKeys<a,b>;
typegotB=DiffKeys<b,a>;
assert<gotA,'x'>(t);
assert<gotB,'z'>(t);
});
ElementwiseIntersect
Takes two objects and returns their element-wise intersection.
Note: this removes any key-level information, such as optional or readonly keys.
test('Can combine two objects elementwise',t=>{
typea={x:number,y:'hi'};
typeb={z:number,y:'there'};
typegot=ElementwiseIntersect<a,b>;
typeexpected={
x:number,
y:'hi'&'there',
z:number,
};
assert<got,expected>(t);
assert<expected,got>(t);
});
test('Can combine two objects with private members elementwise',t=>{
classA{
a:number=1;
privatex:number=2;
y:'hi'='hi';
privatez:'hey'='hey';
}
classB{
a:22=22;
privatex:number=2;
y:'there'='there';
privatez:'friend'='friend';
}
typegot=ElementwiseIntersect<A,B>;
typeexpected={
a:22,
y:'hi'&'there',
};
assert<got,expected>(t);
assert<expected,got>(t);
});
GetKey
Gets the value of specified property on any object without compile time error (Property 'b' does not exist on type '{ a: string; }'.) and the like.
Returns never if the key is not on the object.
It helps to use If<HasKey... to handle validity of the object first.
test('Can safely get the value at a certain key if it exists',t=>{
typeobj={x:number,y:string};
typeexpected=number;
typegot=GetKey<obj,'x'>;
assert<got,expected>(t);
assert<expected,got>(t);
});
test('Will get `never` if key does not exist',t=>{
typeobj={x:number,y:string};
typeexpected=never;
typegot=GetKey<obj,'z'>;
assert<got,expected>(t);
assert<expected,got>(t);
});
HasKey
Returns True if a key, K, is present in a type, T, else False.
Intersect
Returns only the shared properties between two objects.
All shared properties must be the same type.
test('Can get an object with only shared properties',t=>{
typea={x:number,y:string};
typeb={y:string,z:string};
typeexpected={y:string};
typegot=Intersect<a,b>;
assert<got,expected>(t);
assert<expected,got>(t);
});
KeysByType
Gets all keys that point to a given type.
test('Can filter object keys by right side type',t=>{
typeobj={
a:1,
b:2,
c:3,
};
typeexpected='a'|'b';
typegot=KeysByType<obj,1|2>;
assert<got,expected>(t);
assert<expected,got>(t);
});
Merge
Much like _.merge in javascript, this returns an object with all keys present between both objects, but conflicts resolved by rightmost object.
test('Can merge two objects, resolving matching keys by rightmost object',t=>{
typea={x:number,y:string};
typeb={y:number,z:string};
typegot=Merge<a,b>;
typeexpected={x:number,y:number,z:string};
assert<got,expected>(t);
assert<expected,got>(t);
});
test('Can merge an object containing all strings as keys',t=>{
Objects can be indexed by multiple types: string, number, symbol.
For safe compatibility with typescript version, this type will always
have the correct set of object key types for the current version of TS.
This is useful for functions that must take a key, instead of K extends string,
use K extends ObjectKeys.
ObjectType
Takes any type and makes it an object type.
Useful when combined with & intersection types.
test('Can turn an object into another object',t=>{
typeobj={x:number,y:string};
typeexpected=obj;
typegot=ObjectType<obj>;
assert<got,expected>(t);
assert<expected,got>(t);
});
Omit
Gives back an object with listed keys removed.
This is the opposite of Pick.
test('Can omit keys from an object',t=>{
typea={x:number,y:string,z:boolean};
typegot=Omit<a,'x'|'y'>;
typeexpected={z:boolean};
assert<got,expected>(t);
assert<expected,got>(t);
});
Optional
Mark specific keys, K, of T as optional (think Partial).
Can change the types of properties on an object.
This is similar to Merge, except that it will not add previously non-existent properties to the object.
An object with string keys and values of type any.
PureKeys
When an object has optional or readonly keys, that information is contained within the key.
When using optional/readonly keys in another object, they will retain optional/readonly status.
PureKeys will remove the optional/readonly status modifiers from keys.
Required
Mark specific keys, K, of T as required.
test('Can make certain fields of options object required',t=>{
typex={a?:string,b:number|undefined};
typegot1=Required<x,'a'>;
typegot2=Required<x,'b'>;
typegot3=Required<x,'a'|'b'>;
typeexpected1={a:string,b:number|undefined};
typeexpected2={a?:string,b:number};
typeexpected3={a:string,b:number};
assert<got1,expected1>(t);
assert<got2,expected2>(t);
assert<got3,expected3>(t);
});
SharedKeys
Gets all of the keys that are shared between two objects.
test('Can get keys that are same between objects',t=>{
typea={x:number,y:string};
typeb={x:string,y:string,z:boolean};
typegot=SharedKeys<a,b>;
typeexpected='x'|'y';
assert<got,expected>(t);
assert<expected,got>(t);
});
StrictUnion
Makes a union 'strict', such that members are disallowed from including the keys of other members
For example, {x: 1, y: 1} is a valid member of {x: number} | {y: number},
but it's not a valid member of StrictUnion<{x: number} | {y: number}>.
test('disallow union members with mixed properties',t=>{
Typescript 2.9 introduced number | symbol as possible results from keyof any.
For backwards compatibility with objects containing only string keys, this will
exclude any number | symbol keys from keyof.
TaggedObject
For discriminated unions of objects, it is important to have a single "tag" property.
Creates an object with each entry being tagged by the key defining that entry.
TryKey
Like GetKey, but returns unknown if the key is not present on the object.
UnionizeProperties
Get a union of the properties of an object.
test('Can get a union of all values in an object',t=>{
typea={x:'hi',y:'there',z:'friend'};
typegot=UnionizeProperties<a>;
typeexpected='hi'|'there'|'friend';
assert<got,expected>(t);
assert<expected,got>(t);
});
UnionKeys
test('Can get all keys between objects in a union',t=>{
typea={w:number,x:string};
typeb={x:number,z:boolean};
typec={y:boolean,z:string};
typegot=UnionKeys<a|b|c>;
typeexpected='w'|'x'|'y'|'z';
assert<got,expected>(t);
assert<expected,got>(t);
});
Utils
NoDistribute
Prevent T from being distributed in a conditional type.
A conditional is only distributed when the checked type is naked type param and T & {} is not a
naked type param, but has the same contract as T.
test("can create a conditional type that won't distribute over unions",t=>{
Constructs a nominal type of type T.
Useful to prevent any value of type T from being used or modified in places it shouldn't (think ids).
test('Can make a new nominal type',t=>{
typeId=Nominal<string,'id'>;
//TODO: improve once negative testing is in place
assert<Id,Nominal<string,'id'>>(t);
});
Nullable
Mark a type as nullable (null | undefined).
test('Will make a type nullable (null | undefined)',t=>{
typegot=Nullable<string>;
typeexpected=string|null|undefined;
assert<got,expected>(t);
});
test('Will make a type not nullable',t=>{
typegot=NonNullable<Nullable<string>>;
assert<got,string>(t);
});
PromiseOr
Returns the given type or a Promise containing that type.
test('Will give back a promise containing given type union the type itself',t=>{
typegot=PromiseOr<string>;
typeexpected=Promise<string>|string;
assert<got,expected>(t);
});
UnionToIntersection
Defines an intersection type of all union items.
test('Union of Strings',t=>{
typegot=UnionToIntersection<'hi'|'there'>;
typeexpected='hi'&'there';
assert<got,expected>(t);
});
test('Union of Objects',t=>{
typegot=UnionToIntersection<{a:0}|{b:1}|{c:2}>;
typeexpected={
a:0,
b:1,
c:2,
};
assert<got,expected>(t);
});
Functions
AnyFunc
Concisely and cleanly define an arbitrary function.
Useful when designing many api's that don't care what function they take in, they just need to know what it returns.
test('Can define the type of a function that takes any arguments',t=>{
typegot=AnyFunc;
typegot2=AnyFunc<number>;// takes anything, returns a number
typeexpected=(...args:any[])=>any;
typeexpected2=(...args:any[])=>number;
assert<got,expected>(t);
assert<got2,expected2>(t);
});
ArgsAsTuple
Returns a tuple type of a functions arguments up to 7.
test("Can get a tuple of function's argument types",t=>{
typeF0=()=>any;
typeF1=(x:number)=>any;
typeF2=(x:number,y:string)=>any;
typeF3=(x:number,y:string,z:boolean)=>any;
typeE0=[];
typeE1=[number];
typeE2=[number,string];
typeE3=[number,string,boolean];
assert<ArgsAsTuple<F0>,E0>(t);
assert<ArgsAsTuple<F1>,E1>(t);
assert<ArgsAsTuple<F2>,E2>(t);
assert<ArgsAsTuple<F3>,E3>(t);
});
ConstructorFunction
This represents the constructor for a particular object.
test('Can build a constructor type for a type',t=>{
Type guard for any key, k.
Marks k as a key of T if k is in obj.
test('Can check if an object contains a key',t=>{
consto={a:'hi',b:22};
constkey1:string='a';
if(isKeyOf(o,key1)){
assert<typeofkey1,'a'|'b'>(t);
t.pass();
}else{
assert<typeofkey1,string>(t);
t.fail();
}
});
objectKeys
Same as Object.keys except that the returned type is an array of keys of the object.
Note that for the same reason that Object.keys does not do this natively, this method is not safe for objects on the perimeter of your code (user input, read in files, network requests etc.).
test('Can get keys of an object',t=>{
consto={a:'hi',b:22};
constkeys=objectKeys(o);
typeK=typeofkeys;
typeexpected=Array<'a'|'b'>;
assert<K,expected>(t);
assert<expected,K>(t);
t.deepEqual(keys,['a','b']);
});
Readonly
Useful for marking object literals as readonly while still keeping type inference:
const obj = Readonly({ a: 22, b: 'yellow' });
taggedObject
Useful for tagged unions of objects (imagine redux reducers) this tags every sub-object with the key pointing to that sub-object.