UNPKG

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