forked from sindresorhus/type-fest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexact.d.ts
62 lines (47 loc) · 2.76 KB
/
exact.d.ts
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
import type {KeysOfUnion, ArrayElement, ObjectValue} from './internal';
import type {Opaque} from './opaque';
import type {IsEqual} from './is-equal';
/**
Create a type from `ParameterType` and `InputType` and change keys exclusive to `InputType` to `never`.
- Generate a list of keys that exists in `InputType` but not in `ParameterType`.
- Mark these excess keys as `never`.
*/
type ExactObject<ParameterType, InputType> = {[Key in keyof ParameterType]: Exact<ParameterType[Key], ObjectValue<InputType, Key>>}
& Record<Exclude<keyof InputType, KeysOfUnion<ParameterType>>, never>;
/**
Create a type that does not allow extra properties, meaning it only allows properties that are explicitly declared.
This is useful for function type-guarding to reject arguments with excess properties. Due to the nature of TypeScript, it does not complain if excess properties are provided unless the provided value is an object literal.
*Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/12936) if you want to have this type as a built-in in TypeScript.*
@example
```
type OnlyAcceptName = {name: string};
function onlyAcceptName(arguments_: OnlyAcceptName) {}
// TypeScript complains about excess properties when an object literal is provided.
onlyAcceptName({name: 'name', id: 1});
//=> `id` is excess
// TypeScript does not complain about excess properties when the provided value is a variable (not an object literal).
const invalidInput = {name: 'name', id: 1};
onlyAcceptName(invalidInput); // No errors
```
Having `Exact` allows TypeScript to reject excess properties.
@example
```
import {Exact} from 'type-fest';
type OnlyAcceptName = {name: string};
function onlyAcceptNameImproved<T extends Exact<OnlyAcceptName, T>>(arguments_: T) {}
const invalidInput = {name: 'name', id: 1};
onlyAcceptNameImproved(invalidInput); // Compilation error
```
[Read more](https://stackoverflow.com/questions/49580725/is-it-possible-to-restrict-typescript-object-to-contain-only-properties-defined)
@category Utilities
*/
export type Exact<ParameterType, InputType> =
IsEqual<ParameterType, InputType> extends true ? ParameterType
// Convert union of array to array of union: A[] & B[] => (A & B)[]
: ParameterType extends unknown[] ? Array<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
// In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray.
: ParameterType extends readonly unknown[] ? ReadonlyArray<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
// For Opaque types, internal details are hidden from public, so let's leave it as is.
: ParameterType extends Opaque<infer OpaqueType, infer OpaqueToken> ? ParameterType
: ParameterType extends object ? ExactObject<ParameterType, InputType>
: ParameterType;