UNPKG

2.29 kBTypeScriptView Raw
1import type {Opaque} from './opaque';
2
3declare const invariantBrand: unique symbol;
4
5/**
6Create an [invariant type](https://basarat.gitbook.io/typescript/type-system/type-compatibility#footnote-invariance), which is a type that does not accept supertypes and subtypes.
7
8Use-case:
9- Prevent runtime errors that may occur due to assigning subtypes to supertypes.
10- Improve type signature of object methods like [`Object.keys()` or `Object.entries()`](https://github.com/microsoft/TypeScript/pull/12253#issuecomment-263132208) by sealing the object type.
11
12@example
13```
14import type {InvariantOf} from 'type-fest';
15
16class Animal {
17 constructor(public name: string){}
18}
19
20class Cat extends Animal {
21 meow() {}
22}
23
24let animalArray: Animal[] = [animal];
25let catArray: Cat[] = [cat];
26
27animalArray = catArray; // Okay if covariant
28animalArray.push(new Animal('another animal')); // Pushed an animal into catArray
29catArray.forEach(c => c.meow()); // Allowed but, error at runtime
30
31let invariantAnimalArray: InvariantOf<Animal>[] = [animal] as InvariantOf<Animal>[];
32let invariantCatArray: InvariantOf<Cat>[] = [cat] as InvariantOf<Cat>[];
33
34invariantAnimalArray = invariantCatArray; // Error: Type 'InvariantOf<Cat>[]' is not assignable to type 'InvariantOf<Animal>[]'.
35```
36
37@example
38```
39import type {InvariantOf} from 'type-fest';
40
41// In covariance (default)
42
43interface FooBar {
44 foo: number;
45 bar: string
46}
47
48interface FooBarBaz extends FooBar {
49 baz: boolean
50}
51
52declare const fooBar: FooBar
53declare const fooBarBaz: FooBarBaz
54
55function keyOfFooBar(fooBar: FooBar) {
56 return Object.keys(fooBar) as (keyof FooBar)[]
57}
58
59keyOfFooBar(fooBar) //=> (keyof FooBar)[]
60keyOfFooBar(fooBarBaz) //=> (keyof FooBar)[] but, (keyof FooBarBaz)[] at runtime
61
62// In invariance
63
64export function invariantOf<Type>(value: Type): InvariantOf<Type> {
65 return value as InvariantOf<Type>;
66}
67
68function keyOfInvariantFooBar(fooBar: InvariantOf<FooBar>) {
69 return Object.keys(fooBar) as (keyof FooBar)[]
70}
71
72keyOfInvariantFooBar(invariantOf(fooBar)); // (keyof FooBar)[]
73keyOfInvariantFooBar(invariantOf(fooBarBaz)); // Error: Argument of type 'InvariantOf<FooBarBaz>' is not assignable to parameter of type 'InvariantOf<FooBar>'.
74```
75
76@category Type
77*/
78export type InvariantOf<Type> = Type & {[invariantBrand]: (_: Type) => Type};