-
-
Notifications
You must be signed in to change notification settings - Fork 557
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f19a002
commit 51ec78e
Showing
4 changed files
with
112 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,72 @@ | ||
import type {NonRecursiveType} from './internal'; | ||
import type {Paths} from './paths'; | ||
import type {SimplifyDeep} from './simplify-deep'; | ||
|
||
/** | ||
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: You want to define as required only a some nested keys inside a model. | ||
@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 | ||
? never | ||
: SimplifyDeep< | ||
BaseType extends Array<infer _> | ||
? Array< | ||
SetRequiredDeep< | ||
NonNullable<BaseType[number]>, | ||
KeyPaths extends `${number}.${infer Rest extends Paths<NonNullable<BaseType[number]>>}` | ||
? Rest | ||
: never | ||
> | ||
> | ||
: Omit<BaseType, RootRequiredKeys<BaseType, KeyPaths>> & | ||
Required< | ||
Pick< | ||
BaseType, | ||
Extract<KeyPaths, RootRequiredKeys<BaseType, KeyPaths>> | ||
> | ||
> & Partial<{ | ||
[K in RootRequiredKeys< | ||
BaseType, | ||
Exclude<KeyPaths, RootRequiredKeys<BaseType, KeyPaths>> | ||
>]: SetRequiredDeep< | ||
NonNullable<BaseType[K]>, | ||
KeyPaths extends `${K}.${infer Rest extends Paths<NonNullable<BaseType[K]>>}` | ||
? Rest | ||
: never | ||
>; | ||
}> | ||
>; | ||
|
||
// Extract the BaseType root keys from the KeyPaths | ||
type RootRequiredKeys<BaseType, KeyPaths extends Paths<BaseType>> = Extract< | ||
keyof BaseType, | ||
KeyPaths extends `${infer Root}.${string}` ? Root : KeyPaths | ||
>; |
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,38 @@ | ||
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); |