-
-
Notifications
You must be signed in to change notification settings - Fork 569
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Sindre Sorhus <[email protected]> Co-authored-by: Som Shekhar Mukherjee <[email protected]>
- Loading branch information
1 parent
eff76da
commit 3d54627
Showing
4 changed files
with
110 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type {NonRecursiveType, StringToNumber} from './internal'; | ||
import type {Paths} from './paths'; | ||
import type {SimplifyDeep} from './simplify-deep'; | ||
import type {UnknownArray} from './unknown-array'; | ||
|
||
/** | ||
Create a type that makes the given keys required. You can specify deeply nested key paths. The remaining keys are kept as is. | ||
Use-case: Selectively make nested properties required in complex types like models. | ||
@example | ||
``` | ||
import type {SetRequiredDeep} from 'type-fest'; | ||
type Foo = { | ||
a?: number; | ||
b?: string; | ||
c?: { | ||
d?: number | ||
}[] | ||
} | ||
type SomeRequiredDeep = SetRequiredDeep<Foo, 'a' | `c.${number}.d`>; | ||
// type SomeRequiredDeep = { | ||
// a: number; // Is now required | ||
// b?: string; | ||
// c: { | ||
// d: number // Is now required | ||
// }[] | ||
// } | ||
``` | ||
@category Object | ||
*/ | ||
export type SetRequiredDeep<BaseType, KeyPaths extends Paths<BaseType>> = | ||
BaseType extends NonRecursiveType | ||
? BaseType | ||
: SimplifyDeep<( | ||
BaseType extends UnknownArray | ||
? {} | ||
: {[K in keyof BaseType as K extends (KeyPaths | StringToNumber<KeyPaths & string>) ? K : never]-?: BaseType[K]} | ||
) & { | ||
[K in keyof BaseType]: Extract<KeyPaths, `${K & (string | number)}.${string}`> extends never | ||
? BaseType[K] | ||
: SetRequiredDeep<BaseType[K], KeyPaths extends `${K & (string | number)}.${infer Rest extends Paths<BaseType[K]>}` ? Rest : never> | ||
}>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import {expectType} from 'tsd'; | ||
import type {SetRequiredDeep} from '../index'; | ||
|
||
// Set nested key to required | ||
declare const variation1: SetRequiredDeep<{a?: number; b?: {c?: string}}, 'b.c'>; | ||
expectType<{a?: number; b?: {c: string}}>(variation1); | ||
|
||
// Set key to required but not nested keys if not specified | ||
declare const variation2: SetRequiredDeep<{a?: number; b?: {c?: string}}, 'b'>; | ||
expectType<{a?: number; b: {c?: string}}>(variation2); | ||
|
||
// Set root key to required | ||
declare const variation3: SetRequiredDeep<{a?: number; b?: {c?: string}}, 'a'>; | ||
expectType<{a: number; b?: {c?: string}}>(variation3); | ||
|
||
// Keeps required key as required | ||
declare const variation4: SetRequiredDeep<{a: number; b?: {c?: string}}, 'a'>; | ||
expectType<{a: number; b?: {c?: string}}>(variation4); | ||
|
||
// Set key to required in a union. | ||
declare const variation5: SetRequiredDeep<{a?: '1'; b?: {c?: boolean}} | {a?: '2'; b?: {c?: boolean}}, 'a'>; | ||
expectType<{a: '1'; b?: {c?: boolean}} | {a: '2'; b?: {c?: boolean}}>(variation5); | ||
|
||
// Set array key to required | ||
declare const variation6: SetRequiredDeep<{a?: Array<{b?: number}>}, 'a'>; | ||
expectType<{a: Array<{b?: number}>}>(variation6); | ||
|
||
// Set key inside array to required | ||
declare const variation7: SetRequiredDeep<{a?: Array<{b?: number}>}, `a.${number}.b`>; | ||
expectType<{a?: Array<{b: number}>}>(variation7); | ||
|
||
// Set only specified keys inside array to required | ||
declare const variation8: SetRequiredDeep<{a?: Array<{b?: number; c?: string}>}, `a.${number}.b`>; | ||
expectType<{a?: Array<{b: number; c?: string}>}>(variation8); | ||
|
||
// Can set both root and nested keys to required | ||
declare const variation9: SetRequiredDeep<{a?: number; b?: {c?: string}}, 'b' | 'b.c'>; | ||
expectType<{a?: number; b: {c: string}}>(variation9); | ||
|
||
// Preserves required root keys | ||
declare const variation10: SetRequiredDeep<{a: 1; b: {c?: 1}}, 'b.c'>; | ||
expectType<{a: 1; b: {c: 1}}>(variation10); | ||
|
||
// Preserves union in root keys | ||
declare const variation11: SetRequiredDeep<{a: 1; b: {c?: 1} | number}, 'b.c'>; | ||
expectType<{a: 1; b: {c: 1} | number}>(variation11); | ||
|
||
// Preserves readonly in root keys | ||
declare const variation12: SetRequiredDeep<{a: 1; readonly b: {c?: 1}}, 'b.c'>; | ||
expectType<{a: 1; readonly b: {c: 1}}>(variation12); | ||
|
||
// Works with number keys | ||
declare const variation13: SetRequiredDeep<{0: 1; 1: {2?: string}}, '1.2'>; | ||
expectType<{0: 1; 1: {2: string}}>(variation13); | ||
|
||
// Multiple keys | ||
declare const variation14: SetRequiredDeep<{a?: 1; b?: {c?: 2}; d?: {e?: {f?: 2}; g?: 3}}, 'a' | 'b' | 'b.c' | 'd.e.f' | 'd.g'>; | ||
expectType<{a: 1; b: {c: 2}; d?: {e?: {f: 2}; g: 3}}>(variation14); | ||
|
||
// Index signatures | ||
declare const variation15: SetRequiredDeep<{[x: string]: any; a?: number; b?: {c?: number}}, 'a' | 'b.c'>; | ||
expectType<{[x: string]: any; a: number; b?: {c: number}}>(variation15); |