@squall.io/types v0.0.9
@squall.io/types
Utility types to extends TypeScript capabilities.
Why?
TypeScript team's philosophy is not to add more utility type: it divides the community.
We have decided to not add any additional utility types unless they are needed for declaration emit. Every time we add a new utility type, there are ~four different forms to choose from, people split roughly equally on which is "best", and then 75% of people get mad that we added the "wrong" one and blocked them from adding the "right" one to their project's global scope - given that situation, we think it's better for people to just add whichever utility types they need to their own project.
Yet, there are many types that we repeat across our projects. Some of them are simple to write out, others are just cryptic - so to say.
Having those types in a separate package and made them widely available would have at least two effects:
- We no more need to crack our head to craft some types
- We don't have to maintain those type ourselves, per project
Note: We do care to keep your properties modifiers, like optional and readonly.
What's In?
Constructor<T ?= any, P ?= any[]>
Describe a constructor signature, by its instance type T
and its arguments P
.
type Coordinate = { longitude: number, latitude: number };
type CoordinateConstructor = Constructor<Coordinate, [ longitude: number, latitude: number ]>
// Good
const EarthCoordinate: CoordinateConstructor = class {
public constructor(
public longitude: number;
public latitude: number;
) {}
}
// Error: Missing instance properties "longitude" and "latitude"
const MarsCoordinate: CoordinateConstructor = class {
public constructor(
longitude: number;
latitude: number;
) {}
}
// Error: Type "string" is not assignable to type "number"
const NeptuneCoordinate: CoordinateConstructor = class {
public longitude!: number;
public latitude!: number;
public constructor( weight: string ) {}
}
KeyOf<U, V ?= any>
For every member of the union U
, return its keys that map to V
.
Union distribution
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
type Result = keyof Union; // never
type ThisResult = KeyOf<Union>; // 0 | "a" | "A" | 1
Filter keys mapping to a type
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = KeyOf<Union, 'A'>; // "A" | 1
OmitWhen<U, V ?= any, K ?= KeyOf<U, V>>
For every member of the union U
, remove its keys K
when those keys map to V
.
Union distribution
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
type Result = Omit<Union, 'a'>; // {}
type ThisResult = OmitWhen<Union, any, 'a'>; // { readonly A?: 'A' }
Remove keys mapping to a type
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = OmitWhen<Union, 'a'>; // { readonly A?: 'A' } | { 1?: 'A' }
OptionalKeys<U, V ?= any>
For every member of the union U
, return the optional keys that map to V
.
type Letter = { a: 'a', readonly A?: 'A' };
// TypeScript | Not Applicable
type ThisResult = OptionalKeys<Letter>; // "A"
Union distribution
type Input = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = OptionalKeys<Input>; // "A" | 1
Retain optional keys mapping to a type
type Input = { a: 'a', readonly A: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = OptionalKeys<Input, 'A'>; // 1
PickWhen<U, V ?= any, K ?= KeyOf<U, V>>
For every member of the union U
, retain its keys K
when those keys which map to V
.
Union distribution
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
type Result = Pick<Union, 'A'>; // Error: Type 'string' does not satisfy the constraint 'never'.
type ThisResult = PickWhen<Union, any, 'A'>; // { readonly A?: 'A' } | {}
Retain keys mapping to a type
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = PickWhen<Union, 'A'>; // { readonly A?: 'A' } | { 1?: 'A' }
RequiredKeys<U, V ?= any>
For every member of the Union U
, returns the required keys that map to V
.
type Letter = { a: 'a', readonly A?: 'A' };
// TypeScript | Not Applicable
type ThisResult = RequiredKeys<Letter>; // "a"
Union distribution
type Input = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = RequiredKeys<Input>; // "a" | 0
Retain required keys mapping to a type
type Input = { a: 'a', readonly A: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = RequiredKeys<Input, 'A'>; // 1
UnionToIntersection<U>
Merge distinct members of the union U
into a single type.
type Input = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
// TypeScript | Not Applicable
type ThisResult = UnionToIntersection<Input>; // { a: 'a', readonly A?: 'A' } & { 0: 'a', 1?: 'A' }
ValueOf<U>
For every member of the union U
, return the value types to which its keys map.
type Union = { a: 'a', readonly A?: 'A' } | { 0: 'a', 1?: 'A' };
type Result = Union[keyof Union]; // never
type ThisResult = ValueOf<Union>; // "a" | "A" | undefined