Created
April 19, 2023 05:43
-
-
Save justsanjit/21dc05e9ba00af4f7218439f687be034 to your computer and use it in GitHub Desktop.
Typescript mapped type that will accept a nested object and a list of paths to make required.
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
interface Phone { | |
number: string | |
extension?: string | |
} | |
interface Person { | |
firstName?: string; | |
lastName?: string; | |
age?: number; | |
address?: { | |
street?: string; | |
city?: string; | |
zip?: number; | |
}, | |
phones?: Phone[] | |
} | |
type Expand<T> = T extends ReadonlyArray<unknown> ? Expand<T[number]>[] : T extends object ? { [K in keyof T]: Expand<T[K]> } : T; | |
type TopLevelPath<T extends string> = T extends `${infer U}.${infer _}` ? U : T; | |
type PathsToTopLevel<T extends ReadonlyArray<string>> = { | |
[K in keyof T]: TopLevelPath<T[K]>; | |
}; | |
type RemoveTopLevelPath<T extends ReadonlyArray<string>> = { | |
[K in keyof T]: T[K] extends `${infer _}.${infer V}` ? V : never; | |
}; | |
type OmitProperties<T, K extends ReadonlyArray<string>> = { | |
[P in keyof T as P extends K ? never : P]: T[P]; | |
}; | |
type RecursiveRequired<T, K extends ReadonlyArray<string>> = Omit<T, PathsToTopLevel<K>[number]> & { | |
[P in PathsToTopLevel<K>[number]] : T[P & keyof T] extends Array<infer U> | |
? RecursiveRequired<U, RemoveTopLevelPath<K>>[] | |
: T[P & keyof T] extends string | |
? string | |
: T[P & keyof T] extends Object | |
? RecursiveRequired<T[P & keyof T], RemoveTopLevelPath<K>> | |
: T[P & keyof T] | |
} | |
type PersonWithRequiredFields = Expand<RecursiveRequired<Person, ['firstName', 'phones', 'phones.extension', 'age']>> | |
// const p: PersonWithRequiredFields = { | |
// firstName: 'Sanjit'; // Required | |
// lastName: string; // Optional | |
// age: number; // Optional | |
// address?: { // Optional | |
// street?: string; // Optional | |
// city?: string; // Optional | |
// zip?: number; // Optional | |
// }, | |
// phones: { // Required | |
// number: '123456789' // Optional | |
// extension: '123' // Required | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Typescript playground link