Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
30 changes: 30 additions & 0 deletions source/unwrap-required.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
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 1: If the provided type isn’t of `Required<T>`, `UnwrapRequired` has no effect on the original type.

Note 2: `Required<T>` is lossy for tuples with optional elements. `UnwrapRequired` cannot recover the original tuple type in these cases. Object types with optional properties are unaffected by this limitation (and work as expected with `UnwrapRequired`).

@category Object
*/
export type UnwrapRequired<RequiredObjectType> =
Comment thread
porada marked this conversation as resolved.
RequiredObjectType extends Required<infer ObjectType>
? ObjectType
: RequiredObjectType;

export {};
58 changes: 58 additions & 0 deletions test-d/unwrap-required.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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>);

// `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[]>);