Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type {OmitIndexSignature} from './source/omit-index-signature.d.ts';
export type {PickIndexSignature} from './source/pick-index-signature.d.ts';
export type {PartialDeep, PartialDeepOptions} from './source/partial-deep.d.ts';
export type {UnwrapPartial} from './source/unwrap-partial.d.ts';
export type {UnwrapRequired} from './source/unwrap-required.d.ts';
export type {RequiredDeep} from './source/required-deep.d.ts';
export type {PickDeep} from './source/pick-deep.d.ts';
export type {OmitDeep} from './source/omit-deep.d.ts';
Expand Down
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Click the type names for complete docs.
- [`PartialOnUndefinedDeep`](source/partial-on-undefined-deep.d.ts) - Create a deep version of another type where all keys accepting `undefined` type are set to optional.
- [`UndefinedOnPartialDeep`](source/undefined-on-partial-deep.d.ts) - Create a deep version of another type where all optional keys are set to also accept `undefined`.
- [`UnwrapPartial`](source/unwrap-partial.d.ts) - Revert the `Partial` modifier on an object type.
- [`UnwrapRequired`](source/unwrap-required.d.ts) - Revert the `Required` modifier on an object type.
- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of another type.
- [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union.
- [`Tagged`](source/tagged.d.ts) - Create a [tagged type](https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d) that can support [multiple tags](https://github.com/sindresorhus/type-fest/issues/665) and [per-tag metadata](https://medium.com/@ethanresnick/advanced-typescript-tagged-types-improved-with-type-level-metadata-5072fc125fcf).
Expand Down Expand Up @@ -534,6 +535,8 @@ There are many advanced types most users don't know about.
email: 'ex@mple.com',
});
```

`Required<T>` can be reverted with [`UnwrapRequired`](source/unwrap-required.d.ts).
</details>

- [`Readonly<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) - Make all properties in `T` readonly.
Expand Down
37 changes: 37 additions & 0 deletions source/unwrap-required.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type {MapsSetsOrArrays, NonRecursiveType} from './internal/type.d.ts';

/**
Revert the `Required` modifier on an object type.

Use-case: Infer the underlying type `T` when only `Required<T>` is available or the original type may not be directly accessible.

@example
```
import type {UnwrapRequired} from 'type-fest';

type ContactFormData = Required<{
email: string;
message?: string;
}>;

type DraftContactFormData = UnwrapRequired<ContactFormData>;
//=> {email: string; message?: string}
```

Note:
- If the provided type isn’t of the form `Required<T>`, `UnwrapRequired` simply returns the input type.
- `UnwrapRequired` doesn't work with arrays, if instantiated with arrays, it simply returns the input type.

@category Object
*/
export type UnwrapRequired<RequiredObjectType> =
Comment thread
porada marked this conversation as resolved.
RequiredObjectType extends NonRecursiveType | MapsSetsOrArrays
? RequiredObjectType
: _UnwrapRequired<RequiredObjectType>;

type _UnwrapRequired<RequiredObjectType> =
RequiredObjectType extends Required<infer ObjectType>
? ObjectType
: RequiredObjectType;

export {};
73 changes: 73 additions & 0 deletions test-d/unwrap-required.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {expectType} from 'tsd';
import type {UnwrapRequired} from '../index.d.ts';

type TestType = {
a?: string;
b: number;
};

expectType<TestType>({} as UnwrapRequired<Required<TestType>>);

// `UnwrapRequired` preserves optional properties
type AnotherTestType = {
c?: boolean;
d?: 'literal';
};

type TestTypeWithOptionalProps = TestType & AnotherTestType;

expectType<TestTypeWithOptionalProps>({} as UnwrapRequired<Required<TestTypeWithOptionalProps>>);

// `UnwrapRequired` preserves nested `Required` properties
type TestTypeWithRequiredProp = TestType & {
c: Required<AnotherTestType>;
};

expectType<TestTypeWithRequiredProp>({} as UnwrapRequired<Required<TestTypeWithRequiredProp>>);

// `UnwrapRequired` preserves readonly properties
type TestTypeWithReadonlyProps = {
readonly a?: string;
readonly b: number;
readonly c?: boolean;
};

expectType<TestTypeWithReadonlyProps>({} as UnwrapRequired<Required<TestTypeWithReadonlyProps>>);

// `UnwrapRequired` works with methods
type TestTypeWithMethod = {
a?: string;
c?(): void;
};

expectType<TestTypeWithMethod>({} as UnwrapRequired<Required<TestTypeWithMethod>>);

// `UnwrapRequired` works with union types
type RequiredUnionType = Required<TestType> | Required<AnotherTestType>;

expectType<TestType | AnotherTestType>({} as UnwrapRequired<RequiredUnionType>);
expectType<TestType | AnotherTestType>({} as UnwrapRequired<Required<TestType> | AnotherTestType>);
expectType<TestType | string>({} as UnwrapRequired<Required<TestType> | string>);
expectType<TestType | Map<string, string>>({} as UnwrapRequired<Required<TestType> | Map<string, string>>);
expectType<TestType | Map<string, string> | string>({} as UnwrapRequired<Required<TestType> | Map<string, string> | string>);

// `UnwrapRequired` works with unknown types
expectType<unknown>({} as UnwrapRequired<Required<unknown>>);
expectType<any>({} as UnwrapRequired<Required<any>>);

// `UnwrapRequired` has no effect on non-required types
expectType<TestType>({} as UnwrapRequired<TestType>);
expectType<{a: string; b: number}>({} as UnwrapRequired<{a: string; b: number}>);
expectType<readonly string[]>({} as UnwrapRequired<readonly string[]>);
expectType<readonly [string, number?]>({} as UnwrapRequired<readonly [string, number?]>);
expectType<Set<string>>({} as UnwrapRequired<Set<string>>);
expectType<ReadonlySet<string>>({} as UnwrapRequired<ReadonlySet<string>>);
expectType<WeakSet<{a: string}>>({} as UnwrapRequired<WeakSet<{a: string}>>);
expectType<Map<string, string>>({} as UnwrapRequired<Map<string, string>>);
expectType<ReadonlyMap<string, string>>({} as UnwrapRequired<ReadonlyMap<string, string>>);
expectType<WeakMap<{a: string}, string>>({} as UnwrapRequired<WeakMap<{a: string}, string>>);
expectType<Date>({} as UnwrapRequired<Date>);
expectType<RegExp>({} as UnwrapRequired<RegExp>);
expectType<Promise<string>>({} as UnwrapRequired<Promise<string>>);
expectType<string>({} as UnwrapRequired<string>);
expectType<() => string>({} as UnwrapRequired<() => string>);