1 | import type {Opaque} from './opaque';
|
2 |
|
3 | declare const invariantBrand: unique symbol;
|
4 |
|
5 | /**
|
6 | Create 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 |
|
8 | Use-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 | ```
|
14 | import type {InvariantOf} from 'type-fest';
|
15 |
|
16 | class Animal {
|
17 | constructor(public name: string){}
|
18 | }
|
19 |
|
20 | class Cat extends Animal {
|
21 | meow() {}
|
22 | }
|
23 |
|
24 | let animalArray: Animal[] = [animal];
|
25 | let catArray: Cat[] = [cat];
|
26 |
|
27 | animalArray = catArray; // Okay if covariant
|
28 | animalArray.push(new Animal('another animal')); // Pushed an animal into catArray
|
29 | catArray.forEach(c => c.meow()); // Allowed but, error at runtime
|
30 |
|
31 | let invariantAnimalArray: InvariantOf<Animal>[] = [animal] as InvariantOf<Animal>[];
|
32 | let invariantCatArray: InvariantOf<Cat>[] = [cat] as InvariantOf<Cat>[];
|
33 |
|
34 | invariantAnimalArray = invariantCatArray; // Error: Type 'InvariantOf<Cat>[]' is not assignable to type 'InvariantOf<Animal>[]'.
|
35 | ```
|
36 |
|
37 | @example
|
38 | ```
|
39 | import type {InvariantOf} from 'type-fest';
|
40 |
|
41 | // In covariance (default)
|
42 |
|
43 | interface FooBar {
|
44 | foo: number;
|
45 | bar: string
|
46 | }
|
47 |
|
48 | interface FooBarBaz extends FooBar {
|
49 | baz: boolean
|
50 | }
|
51 |
|
52 | declare const fooBar: FooBar
|
53 | declare const fooBarBaz: FooBarBaz
|
54 |
|
55 | function keyOfFooBar(fooBar: FooBar) {
|
56 | return Object.keys(fooBar) as (keyof FooBar)[]
|
57 | }
|
58 |
|
59 | keyOfFooBar(fooBar) //=> (keyof FooBar)[]
|
60 | keyOfFooBar(fooBarBaz) //=> (keyof FooBar)[] but, (keyof FooBarBaz)[] at runtime
|
61 |
|
62 | // In invariance
|
63 |
|
64 | export function invariantOf<Type>(value: Type): InvariantOf<Type> {
|
65 | return value as InvariantOf<Type>;
|
66 | }
|
67 |
|
68 | function keyOfInvariantFooBar(fooBar: InvariantOf<FooBar>) {
|
69 | return Object.keys(fooBar) as (keyof FooBar)[]
|
70 | }
|
71 |
|
72 | keyOfInvariantFooBar(invariantOf(fooBar)); // (keyof FooBar)[]
|
73 | keyOfInvariantFooBar(invariantOf(fooBarBaz)); // Error: Argument of type 'InvariantOf<FooBarBaz>' is not assignable to parameter of type 'InvariantOf<FooBar>'.
|
74 | ```
|
75 |
|
76 | @category Type
|
77 | */
|
78 | export type InvariantOf<Type> = Type & {[invariantBrand]: (_: Type) => Type};
|