UNPKG

3.32 kBTypeScriptView Raw
1import type {ArrayElement, ObjectValue} from './internal';
2import type {IsEqual} from './is-equal';
3import type {KeysOfUnion} from './keys-of-union';
4import type {IsUnknown} from './is-unknown';
5import type {Primitive} from './primitive';
6
7/**
8Create a type from `ParameterType` and `InputType` and change keys exclusive to `InputType` to `never`.
9- Generate a list of keys that exists in `InputType` but not in `ParameterType`.
10- Mark these excess keys as `never`.
11*/
12type ExactObject<ParameterType, InputType> = {[Key in keyof ParameterType]: Exact<ParameterType[Key], ObjectValue<InputType, Key>>}
13& Record<Exclude<keyof InputType, KeysOfUnion<ParameterType>>, never>;
14
15/**
16Create a type that does not allow extra properties, meaning it only allows properties that are explicitly declared.
17
18This 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.
19
20*Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/12936) if you want to have this type as a built-in in TypeScript.*
21
22@example
23```
24type OnlyAcceptName = {name: string};
25
26function onlyAcceptName(arguments_: OnlyAcceptName) {}
27
28// TypeScript complains about excess properties when an object literal is provided.
29onlyAcceptName({name: 'name', id: 1});
30//=> `id` is excess
31
32// TypeScript does not complain about excess properties when the provided value is a variable (not an object literal).
33const invalidInput = {name: 'name', id: 1};
34onlyAcceptName(invalidInput); // No errors
35```
36
37Having `Exact` allows TypeScript to reject excess properties.
38
39@example
40```
41import {Exact} from 'type-fest';
42
43type OnlyAcceptName = {name: string};
44
45function onlyAcceptNameImproved<T extends Exact<OnlyAcceptName, T>>(arguments_: T) {}
46
47const invalidInput = {name: 'name', id: 1};
48onlyAcceptNameImproved(invalidInput); // Compilation error
49```
50
51[Read more](https://stackoverflow.com/questions/49580725/is-it-possible-to-restrict-typescript-object-to-contain-only-properties-defined)
52
53@category Utilities
54*/
55export type Exact<ParameterType, InputType> =
56 // Before distributing, check if the two types are equal and if so, return the parameter type immediately
57 IsEqual<ParameterType, InputType> extends true ? ParameterType
58 // If the parameter is a primitive, return it as is immediately to avoid it being converted to a complex type
59 : ParameterType extends Primitive ? ParameterType
60 // If the parameter is an unknown, return it as is immediately to avoid it being converted to a complex type
61 : IsUnknown<ParameterType> extends true ? unknown
62 // If the parameter is a Function, return it as is because this type is not capable of handling function, leave it to TypeScript
63 : ParameterType extends Function ? ParameterType
64 // Convert union of array to array of union: A[] & B[] => (A & B)[]
65 : ParameterType extends unknown[] ? Array<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
66 // In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray.
67 : ParameterType extends readonly unknown[] ? ReadonlyArray<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
68 : ExactObject<ParameterType, InputType>;