Skip to content

Commit 99b0b07

Browse files
poradasom-sm
andauthored
Add UnwrapPartial type (#1296)
Co-authored-by: Som Shekhar Mukherjee <iamssmkhrj@gmail.com>
1 parent dfbefad commit 99b0b07

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type {SingleKeyObject} from './source/single-key-object.d.ts';
3232
export type {OmitIndexSignature} from './source/omit-index-signature.d.ts';
3333
export type {PickIndexSignature} from './source/pick-index-signature.d.ts';
3434
export type {PartialDeep, PartialDeepOptions} from './source/partial-deep.d.ts';
35+
export type {UnwrapPartial} from './source/unwrap-partial.d.ts';
3536
export type {RequiredDeep} from './source/required-deep.d.ts';
3637
export type {PickDeep} from './source/pick-deep.d.ts';
3738
export type {OmitDeep} from './source/omit-deep.d.ts';

readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ Click the type names for complete docs.
122122
- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) if you only need one level deep.
123123
- [`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.
124124
- [`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`.
125+
- [`UnwrapPartial`](source/unwrap-partial.d.ts) - Revert the `Partial` modifier on an object type.
125126
- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype) if you only need one level deep.
126127
- [`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. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729).
127128
- [`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). (This replaces the previous [`Opaque`](source/tagged.d.ts) type, which is now deprecated.)
@@ -475,6 +476,8 @@ There are many advanced types most users don't know about.
475476
// NodeConfig interface.
476477
new NodeAppBuilder().config({appName: 'ToDoApp'});
477478
```
479+
480+
`Partial<T>` can be reverted with [`UnwrapPartial`](source/unwrap-partial.d.ts).
478481
</details>
479482

480483
- [`Required<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) - Make all properties in `T` required.

source/unwrap-partial.d.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
Revert the `Partial` modifier on an object type.
3+
4+
Use case: Infer the underlying type `T` when only `Partial<T>` is available or the original type may not be directly accessible.
5+
6+
@example
7+
```
8+
import type {UnwrapPartial} from 'type-fest';
9+
10+
type Config = Partial<{
11+
port: number;
12+
host: string;
13+
secure?: boolean;
14+
}>;
15+
16+
type InitializedConfig = UnwrapPartial<Config>;
17+
//=> {port: number; host: string; secure?: boolean}
18+
```
19+
20+
Note: If the provided type isn’t of `Partial<T>`, `UnwrapPartial` has no effect on the original type.
21+
22+
@category Object
23+
*/
24+
export type UnwrapPartial<PartialObjectType> =
25+
PartialObjectType extends Partial<infer ObjectType>
26+
? (
27+
Partial<ObjectType> extends PartialObjectType
28+
? ObjectType
29+
: PartialObjectType
30+
)
31+
: PartialObjectType;
32+
33+
export {};

test-d/unwrap-partial.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {expectType} from 'tsd';
2+
import type {EmptyObject, UnwrapPartial} from '../index.d.ts';
3+
4+
type TestType = {
5+
a: string;
6+
b: number;
7+
};
8+
9+
expectType<TestType>({} as UnwrapPartial<Partial<TestType>>);
10+
expectType<TestType>({} as UnwrapPartial<{a?: string; b?: number}>);
11+
expectType<Partial<TestType>>({} as UnwrapPartial<Partial<Partial<TestType>>>);
12+
13+
// `UnwrapPartial` preserves optional properties
14+
type TestTypeWithOptionalProp = TestType & {
15+
c?: boolean;
16+
};
17+
18+
type AnotherTestType = {
19+
c: boolean;
20+
d: 'literal';
21+
};
22+
23+
type TestTypeWithOptionalProps = TestType & Partial<AnotherTestType>;
24+
25+
expectType<TestTypeWithOptionalProp>({} as UnwrapPartial<Partial<TestTypeWithOptionalProp>>);
26+
expectType<TestTypeWithOptionalProps>({} as UnwrapPartial<Partial<TestTypeWithOptionalProps>>);
27+
28+
// `UnwrapPartial` preserves nested `Partial` properties
29+
type TestTypeWithPartialProp = TestType & {
30+
c: Partial<TestType>;
31+
};
32+
33+
expectType<TestTypeWithPartialProp>({} as UnwrapPartial<Partial<TestTypeWithPartialProp>>);
34+
35+
// `UnwrapPartial` preserves readonly properties
36+
type TestTypeWithReadonlyProps = Readonly<TestType> & {
37+
readonly c: boolean;
38+
};
39+
40+
expectType<TestTypeWithReadonlyProps>({} as UnwrapPartial<Partial<TestTypeWithReadonlyProps>>);
41+
42+
// `UnwrapPartial` works with methods
43+
type TestTypeWithMethod = {
44+
c(): void;
45+
};
46+
47+
expectType<TestTypeWithMethod>({} as UnwrapPartial<Partial<TestTypeWithMethod>>);
48+
49+
// `UnwrapPartial` works with union types
50+
type PartialUnionType = Partial<TestType> | Partial<AnotherTestType>;
51+
52+
expectType<TestType | AnotherTestType>({} as UnwrapPartial<PartialUnionType>);
53+
expectType<TestType | AnotherTestType>({} as UnwrapPartial<Partial<TestType> | AnotherTestType>);
54+
55+
// `UnwrapPartial` works with index signatures
56+
type ArrayLikeTestType = {
57+
[index: number]: string;
58+
};
59+
60+
type PlainObjectTestType = {
61+
readonly [key: string]: number | undefined;
62+
};
63+
64+
expectType<ArrayLikeTestType>({} as UnwrapPartial<Partial<ArrayLikeTestType>>);
65+
expectType<PlainObjectTestType>({} as UnwrapPartial<PlainObjectTestType>);
66+
expectType<Record<number, string>>({} as UnwrapPartial<Partial<Record<number, string>>>);
67+
expectType<string[]>({} as UnwrapPartial<Partial<string[]>>);
68+
expectType<Array<string | undefined>>({} as UnwrapPartial<Array<string | undefined>>);
69+
70+
// `UnwrapPartial` works with tuples
71+
type TestTuple = [string, number, boolean];
72+
73+
expectType<TestTuple>({} as UnwrapPartial<Partial<TestTuple>>);
74+
expectType<TestTuple>({} as UnwrapPartial<[string?, number?, boolean?]>);
75+
76+
// `UnwrapPartial` works with empty objects and unknown types
77+
expectType<EmptyObject>({} as UnwrapPartial<Partial<EmptyObject>>);
78+
expectType<unknown>({} as UnwrapPartial<Partial<unknown>>);
79+
expectType<any>({} as UnwrapPartial<Partial<any>>);
80+
expectType<Record<string, unknown>>({} as UnwrapPartial<Partial<Record<string, unknown>>>);
81+
expectType<Record<string, any>>({} as UnwrapPartial<Partial<Record<string, any>>>);

0 commit comments

Comments
 (0)